Checked in initial draft of the new tree widget.
author4 <jrb@redhat.com>
Thu, 5 Oct 2000 01:04:57 +0000 (01:04 +0000)
committerJonathan Blandford <jrb@src.gnome.org>
Thu, 5 Oct 2000 01:04:57 +0000 (01:04 +0000)
2000-10-04    <jrb@redhat.com>

* gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
tree widget.

38 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtkcellrenderer.c [new file with mode: 0644]
gtk/gtkcellrenderer.h [new file with mode: 0644]
gtk/gtkcellrendererpixbuf.c [new file with mode: 0644]
gtk/gtkcellrendererpixbuf.h [new file with mode: 0644]
gtk/gtkcellrenderertext.c [new file with mode: 0644]
gtk/gtkcellrenderertext.h [new file with mode: 0644]
gtk/gtkcellrenderertextpixbuf.c [new file with mode: 0644]
gtk/gtkcellrenderertextpixbuf.h [new file with mode: 0644]
gtk/gtkcellrenderertoggle.c [new file with mode: 0644]
gtk/gtkcellrenderertoggle.h [new file with mode: 0644]
gtk/gtkliststore.c [new file with mode: 0644]
gtk/gtkliststore.h [new file with mode: 0644]
gtk/gtkmodelsimple.c [new file with mode: 0644]
gtk/gtkmodelsimple.h [new file with mode: 0644]
gtk/gtkrbtree.c [new file with mode: 0644]
gtk/gtkrbtree.h [new file with mode: 0644]
gtk/gtktreedatalist.c [new file with mode: 0644]
gtk/gtktreedatalist.h [new file with mode: 0644]
gtk/gtktreemodel.c [new file with mode: 0644]
gtk/gtktreemodel.h [new file with mode: 0644]
gtk/gtktreeprivate.h [new file with mode: 0644]
gtk/gtktreeselection.c [new file with mode: 0644]
gtk/gtktreeselection.h [new file with mode: 0644]
gtk/gtktreestore.c [new file with mode: 0644]
gtk/gtktreestore.h [new file with mode: 0644]
gtk/gtktreeview.c [new file with mode: 0644]
gtk/gtktreeview.h [new file with mode: 0644]
gtk/gtktreeviewcolumn.c [new file with mode: 0644]
gtk/gtktreeviewcolumn.h [new file with mode: 0644]
gtk/treestoretest.c [new file with mode: 0644]
tests/treestoretest.c [new file with mode: 0644]

index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
index 3bb78c444ff84a3b448493d658d6853d8575015d..780d42713bae415391e6f0cb3d8e994eb4e234ed 100644 (file)
@@ -1,3 +1,8 @@
+2000-10-04    <jrb@redhat.com>
+
+       * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+       tree widget.
+
 2000-10-04  Jonathan Blandford  <jrb@redhat.com>
 
        * gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/gtk/gtkcellrenderer.c b/gtk/gtkcellrenderer.c
new file mode 100644 (file)
index 0000000..5a12dc2
--- /dev/null
@@ -0,0 +1,268 @@
+/* gtkcellrenderer.c
+ * Copyright (C) 2000  Red Hat, Inc. Jonathan Blandford
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkcellrenderer.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+static void gtk_cell_renderer_init       (GtkCellRenderer      *cell);
+static void gtk_cell_renderer_class_init (GtkCellRendererClass *class);
+static void gtk_cell_renderer_get_param  (GObject              *object,
+                                         guint                 param_id,
+                                         GValue               *value,
+                                         GParamSpec           *pspec,
+                                         const gchar          *trailer);
+static void gtk_cell_renderer_set_param  (GObject              *object,
+                                         guint                 param_id,
+                                         GValue               *value,
+                                         GParamSpec           *pspec,
+                                         const gchar          *trailer);
+
+
+enum {
+  PROP_ZERO,
+  PROP_XALIGN,
+  PROP_YALIGN,
+  PROP_XPAD,
+  PROP_YPAD
+};
+
+
+GtkType
+gtk_cell_renderer_get_type (void)
+{
+  static GtkType cell_type = 0;
+
+  if (!cell_type)
+    {
+      static const GTypeInfo cell_info =
+      {
+        sizeof (GtkCellRendererClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_cell_renderer_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkCellRenderer),
+       0,
+        (GInstanceInitFunc) gtk_cell_renderer_init,
+      };
+
+      cell_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkCellRenderer", &cell_info);
+    }
+
+  return cell_type;
+}
+
+static void
+gtk_cell_renderer_init (GtkCellRenderer *cell)
+{
+  cell->xpad = 0;
+  cell->ypad = 0;
+  cell->xalign = 0.5;
+  cell->yalign = 0.5;
+}
+
+static void
+gtk_cell_renderer_class_init (GtkCellRendererClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->get_param = gtk_cell_renderer_get_param;
+  object_class->set_param = gtk_cell_renderer_set_param;
+
+  class->render = NULL;
+  class->get_size = NULL;
+
+
+  g_object_class_install_param (object_class,
+                               PROP_XALIGN,
+                               g_param_spec_float ("xalign",
+                                                   _("xalign"),
+                                                   _("The x-align."),
+                                                   0.0,
+                                                   1.0,
+                                                   0.0,
+                                                   G_PARAM_READABLE |
+                                                   G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_YALIGN,
+                               g_param_spec_float ("yalign",
+                                                   _("yalign"),
+                                                   _("The y-align."),
+                                                   0.0,
+                                                   1.0,
+                                                   0.5,
+                                                   G_PARAM_READABLE |
+                                                   G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_XPAD,
+                               g_param_spec_uint ("xpad",
+                                                  _("xpad"),
+                                                  _("The xpad."),
+                                                  0,
+                                                  100,
+                                                  2,
+                                                  G_PARAM_READABLE |
+                                                  G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_YPAD,
+                               g_param_spec_uint ("ypad",
+                                                  _("ypad"),
+                                                  _("The ypad."),
+                                                  0,
+                                                  100,
+                                                  2,
+                                                  G_PARAM_READABLE |
+                                                  G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_get_param (GObject     *object,
+                            guint        param_id,
+                            GValue      *value,
+                            GParamSpec  *pspec,
+                            const gchar *trailer)
+{
+  GtkCellRenderer *cell = GTK_CELL_RENDERER (object);
+
+  switch (param_id)
+    {
+    case PROP_XALIGN:
+      g_value_init (value, G_TYPE_FLOAT);
+      g_value_set_float (value, cell->xalign);
+      break;
+    case PROP_YALIGN:
+      g_value_init (value, G_TYPE_FLOAT);
+      g_value_set_float (value, cell->yalign);
+      break;
+    case PROP_XPAD:
+      g_value_init (value, G_TYPE_INT);
+      g_value_set_float (value, cell->xpad);
+      break;
+    case PROP_YPAD:
+      g_value_init (value, G_TYPE_INT);
+      g_value_set_float (value, cell->ypad);
+      break;
+    default:
+      break;
+    }
+
+}
+
+static void
+gtk_cell_renderer_set_param (GObject     *object,
+                            guint        param_id,
+                            GValue      *value,
+                            GParamSpec  *pspec,
+                            const gchar *trailer)
+{
+  GtkCellRenderer *cell = GTK_CELL_RENDERER (object);
+
+  switch (param_id)
+    {
+    case PROP_XALIGN:
+      cell->xalign = g_value_get_float (value);
+      break;
+    case PROP_YALIGN:
+      cell->yalign = g_value_get_float (value);
+      break;
+    case PROP_XPAD:
+      cell->xpad = g_value_get_int (value);
+      break;
+    case PROP_YPAD:
+      cell->ypad = g_value_get_int (value);
+      break;
+    default:
+      break;
+    }
+}
+
+void
+gtk_cell_renderer_get_size (GtkCellRenderer *cell,
+                           GtkWidget *widget,
+                           gint      *width,
+                           gint      *height)
+{
+  /* It's actually okay to pass in a NULL cell, as we run into that
+   * a lot */
+  if (cell == NULL)
+    return;
+  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+  g_return_if_fail (GTK_CELL_RENDERER_GET_CLASS (cell)->get_size != NULL);
+
+  GTK_CELL_RENDERER_GET_CLASS (cell)->get_size (cell, widget, width, height);
+}
+
+void
+gtk_cell_renderer_render (GtkCellRenderer *cell,
+                         GdkWindow       *window,
+                         GtkWidget       *widget,
+                         GdkRectangle    *background_area,
+                         GdkRectangle    *cell_area,
+                         GdkRectangle    *expose_area,
+                         guint            flags)
+{
+  /* It's actually okay to pass in a NULL cell, as we run into that
+   * a lot */
+  if (cell == NULL)
+    return;
+  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+  g_return_if_fail (GTK_CELL_RENDERER_GET_CLASS (cell)->render != NULL);
+
+  GTK_CELL_RENDERER_GET_CLASS (cell)->render (cell,
+                                             window,
+                                             widget,
+                                             background_area,
+                                             cell_area,
+                                             expose_area,
+                                             flags);
+}
+
+gint
+gtk_cell_renderer_event (GtkCellRenderer *cell,
+                        GdkEvent        *event,
+                        GtkWidget       *widget,
+                        gchar           *path,
+                        GdkRectangle    *background_area,
+                        GdkRectangle    *cell_area,
+                        guint            flags)
+{
+  /* It's actually okay to pass in a NULL cell, as we run into that
+   * a lot */
+  if (cell == NULL)
+    return FALSE;
+  g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE);
+  if (GTK_CELL_RENDERER_GET_CLASS (cell)->event == NULL)
+    return FALSE;
+
+  return GTK_CELL_RENDERER_GET_CLASS (cell)->event (cell,
+                                                   event,
+                                                   widget,
+                                                   path,
+                                                   background_area,
+                                                   cell_area,
+                                                   flags);
+}
+
diff --git a/gtk/gtkcellrenderer.h b/gtk/gtkcellrenderer.h
new file mode 100644 (file)
index 0000000..2b16126
--- /dev/null
@@ -0,0 +1,111 @@
+/* gtkcellrenderer.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_H__
+#define __GTK_CELL_RENDERER_H__
+
+#include <gtk/gtkobject.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+
+#endif /* __cplusplus */
+
+typedef enum
+{
+  GTK_CELL_RENDERER_SELECTED = 1 << 0,
+  GTK_CELL_RENDERER_PRELIT = 1 << 1,
+  GTK_CELL_RENDERER_INSENSITIVE = 1 << 2
+} GtkCellRendererType;
+
+#define GTK_TYPE_CELL_RENDERER           (gtk_cell_renderer_get_type ())
+#define GTK_CELL_RENDERER(obj)           (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRenderer))
+#define GTK_CELL_RENDERER_CLASS(klass)   (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass))
+#define GTK_IS_CELL_RENDERER(obj)        (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER))
+#define GTK_IS_CELL_RENDERER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER))
+#define GTK_CELL_RENDERER_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass))
+
+typedef struct _GtkCellRenderer GtkCellRenderer;
+typedef struct _GtkCellRendererClass GtkCellRendererClass;
+
+struct _GtkCellRenderer
+{
+  GtkObject parent;
+
+  gfloat xalign;
+  gfloat yalign;
+
+  guint16 xpad;
+  guint16 ypad;
+};
+
+struct _GtkCellRendererClass
+{
+  GtkObjectClass parent_class;
+
+  /* vtable - not signals */
+  void (* get_size) (GtkCellRenderer *cell,
+                    GtkWidget       *widget,
+                    gint            *width,
+                    gint            *height);
+  void (* render)   (GtkCellRenderer *cell,
+                    GdkWindow       *window,
+                    GtkWidget       *widget,
+                    GdkRectangle    *background_area,
+                    GdkRectangle    *cell_area,
+                    GdkRectangle    *expose_area,
+                    guint            flags);
+
+  gint (* event)    (GtkCellRenderer *cell,
+                    GdkEvent        *event,
+                    GtkWidget       *widget,
+                    gchar           *path,
+                    GdkRectangle    *background_area,
+                    GdkRectangle    *cell_area,
+                    guint            flags);
+};
+
+
+GtkType gtk_cell_renderer_get_type (void);
+void    gtk_cell_renderer_get_size (GtkCellRenderer *cell,
+                                   GtkWidget       *widget,
+                                   gint            *width,
+                                   gint            *height);
+void    gtk_cell_renderer_render   (GtkCellRenderer *cell,
+                                   GdkWindow       *window,
+                                   GtkWidget       *widget,
+                                   GdkRectangle    *background_area,
+                                   GdkRectangle    *cell_area,
+                                   GdkRectangle    *expose_area,
+                                   guint            flags);
+gint    gtk_cell_renderer_event    (GtkCellRenderer *cell,
+                                   GdkEvent        *event,
+                                   GtkWidget       *widget,
+                                   gchar           *path,
+                                   GdkRectangle    *background_area,
+                                   GdkRectangle    *cell_area,
+                                   guint            flags);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_H__ */
diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c
new file mode 100644 (file)
index 0000000..c37cb5a
--- /dev/null
@@ -0,0 +1,240 @@
+/* gtkcellrendererpixbuf.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gtkcellrendererpixbuf.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+
+static void gtk_cell_renderer_pixbuf_get_param  (GObject                    *object,
+                                                guint                       param_id,
+                                                GValue                     *value,
+                                                GParamSpec                 *pspec,
+                                                const gchar                *trailer);
+static void gtk_cell_renderer_pixbuf_set_param  (GObject                    *object,
+                                                guint                       param_id,
+                                                GValue                     *value,
+                                                GParamSpec                 *pspec,
+                                                const gchar                *trailer);
+static void gtk_cell_renderer_pixbuf_init       (GtkCellRendererPixbuf      *celltext);
+static void gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class);
+static void gtk_cell_renderer_pixbuf_get_size   (GtkCellRenderer            *cell,
+                                                GtkWidget                  *widget,
+                                                gint                       *width,
+                                                gint                       *height);
+static void gtk_cell_renderer_pixbuf_render     (GtkCellRenderer            *cell,
+                                                GdkWindow                  *window,
+                                                GtkWidget                  *widget,
+                                                GdkRectangle               *background_area,
+                                                GdkRectangle               *cell_area,
+                                                GdkRectangle               *expose_area,
+                                                guint                       flags);
+
+
+enum {
+       PROP_ZERO,
+       PROP_PIXBUF
+};
+
+
+GtkType
+gtk_cell_renderer_pixbuf_get_type (void)
+{
+       static GtkType cell_pixbuf_type = 0;
+
+       if (!cell_pixbuf_type)
+       {
+               static const GTypeInfo cell_pixbuf_info =
+               {
+                       sizeof (GtkCellRendererPixbufClass),
+                       NULL,           /* base_init */
+                       NULL,           /* base_finalize */
+                       (GClassInitFunc) gtk_cell_renderer_pixbuf_class_init,
+                       NULL,           /* class_finalize */
+                       NULL,           /* class_data */
+                       sizeof (GtkCellRendererPixbuf),
+                       0,              /* n_preallocs */
+                       (GInstanceInitFunc) gtk_cell_renderer_pixbuf_init,
+               };
+
+               cell_pixbuf_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererPixbuf", &cell_pixbuf_info);
+       }
+
+       return cell_pixbuf_type;
+}
+
+static void
+gtk_cell_renderer_pixbuf_init (GtkCellRendererPixbuf *cellpixbuf)
+{
+}
+
+static void
+gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
+       GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+       object_class->get_param = gtk_cell_renderer_pixbuf_get_param;
+       object_class->set_param = gtk_cell_renderer_pixbuf_set_param;
+
+       cell_class->get_size = gtk_cell_renderer_pixbuf_get_size;
+       cell_class->render = gtk_cell_renderer_pixbuf_render;
+
+       g_object_class_install_param (object_class,
+                                     PROP_PIXBUF,
+                                     g_param_spec_object ("pixbuf",
+                                                          _("Pixbuf Object"),
+                                                          _("The pixbuf to render."),
+                                                          GDK_TYPE_PIXBUF,
+                                                          G_PARAM_READABLE |
+                                                          G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_pixbuf_get_param (GObject        *object,
+                                   guint           param_id,
+                                   GValue         *value,
+                                   GParamSpec     *pspec,
+                                   const gchar    *trailer)
+{
+       GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
+
+       switch (param_id)
+       {
+       case PROP_PIXBUF:
+               g_value_init (value, G_TYPE_OBJECT);
+               g_value_set_object (value, G_OBJECT (cellpixbuf->pixbuf));
+               break;
+       default:
+               break;
+       }
+}
+
+
+static void
+gtk_cell_renderer_pixbuf_set_param (GObject     *object,
+                                   guint        param_id,
+                                   GValue      *value,
+                                   GParamSpec  *pspec,
+                                   const gchar *trailer)
+{
+       GdkPixbuf *pixbuf;
+       GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
+
+       switch (param_id)
+       {
+       case PROP_PIXBUF:
+               pixbuf = GDK_PIXBUF (g_value_get_object (value));
+               g_object_ref (G_OBJECT (pixbuf));
+               if (cellpixbuf->pixbuf)
+                       g_object_unref (G_OBJECT (cellpixbuf->pixbuf));
+               cellpixbuf->pixbuf = pixbuf;
+               break;
+       default:
+               break;
+       }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_pixbuf_new (void)
+{
+       return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_pixbuf_get_type ()));
+}
+
+static void
+gtk_cell_renderer_pixbuf_get_size (GtkCellRenderer *cell,
+                                  GtkWidget       *widget,
+                                  gint            *width,
+                                  gint            *height)
+{
+       GtkCellRendererPixbuf *cellpixbuf = (GtkCellRendererPixbuf *) cell;
+
+       if (width)
+               *width = (gint) GTK_CELL_RENDERER (cellpixbuf)->xpad * 2 +
+                       (cellpixbuf->pixbuf ? gdk_pixbuf_get_width (cellpixbuf->pixbuf) : 0);
+
+       if (height)
+               *height = (gint) GTK_CELL_RENDERER (cellpixbuf)->ypad * 2 +
+                       (cellpixbuf->pixbuf ? gdk_pixbuf_get_height (cellpixbuf->pixbuf) : 0);
+}
+
+static void
+gtk_cell_renderer_pixbuf_render (GtkCellRenderer    *cell,
+                                GdkWindow          *window,
+                                GtkWidget          *widget,
+                                GdkRectangle       *background_area,
+                                GdkRectangle       *cell_area,
+                                GdkRectangle       *expose_area,
+                                guint               flags)
+
+{
+       GtkCellRendererPixbuf *cellpixbuf = (GtkCellRendererPixbuf *) cell;
+       GdkPixbuf *pixbuf;
+       guchar *pixels;
+       gint rowstride;
+       gint real_xoffset;
+       gint real_yoffset;
+       GdkGC *bg_gc = NULL;
+
+       pixbuf = cellpixbuf->pixbuf;
+
+       if (!pixbuf)
+               return;
+
+       if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
+               bg_gc = widget->style->bg_gc [GTK_STATE_SELECTED];
+       else
+               bg_gc = widget->style->base_gc [GTK_STATE_NORMAL];
+
+       gdk_gc_set_clip_rectangle (bg_gc, cell_area);
+
+       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+       pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+       real_xoffset = GTK_CELL_RENDERER (cellpixbuf)->xalign * (cell_area->width - gdk_pixbuf_get_width (pixbuf) - (2 * GTK_CELL_RENDERER (cellpixbuf)->xpad));
+       real_xoffset = MAX (real_xoffset, 0) + GTK_CELL_RENDERER (cellpixbuf)->xpad;
+       real_yoffset = GTK_CELL_RENDERER (cellpixbuf)->yalign * (cell_area->height - gdk_pixbuf_get_height (pixbuf) - (2 * GTK_CELL_RENDERER (cellpixbuf)->ypad));
+       real_yoffset = MAX (real_yoffset, 0) + GTK_CELL_RENDERER (cellpixbuf)->ypad;
+
+       if (gdk_pixbuf_get_has_alpha (pixbuf))
+               gdk_draw_rgb_32_image (window,
+                                      bg_gc,
+                                      cell_area->x + real_xoffset,
+                                      cell_area->y + real_yoffset,
+                                      gdk_pixbuf_get_width (pixbuf),
+                                      gdk_pixbuf_get_height (pixbuf),
+                                      GDK_RGB_DITHER_NORMAL,
+                                      pixels,
+                                      rowstride);
+       else
+               gdk_draw_rgb_image (window,
+                                   bg_gc,
+                                   cell_area->x + real_xoffset,
+                                   cell_area->y + real_yoffset,
+                                   gdk_pixbuf_get_width (pixbuf),
+                                   gdk_pixbuf_get_height (pixbuf),
+                                   GDK_RGB_DITHER_NORMAL,
+                                   pixels,
+                                   rowstride);
+
+       gdk_gc_set_clip_rectangle (bg_gc, NULL);
+}
diff --git a/gtk/gtkcellrendererpixbuf.h b/gtk/gtkcellrendererpixbuf.h
new file mode 100644 (file)
index 0000000..14fd9b5
--- /dev/null
@@ -0,0 +1,58 @@
+/* gtkcellrendererpixbuf.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_PIXBUF_H__
+#define __GTK_CELL_RENDERER_PIXBUF_H__
+
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_PIXBUF                  (gtk_cell_renderer_pixbuf_get_type ())
+#define GTK_CELL_RENDERER_PIXBUF(obj)                  (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF, GtkCellRendererPixbuf))
+#define GTK_CELL_RENDERER_PIXBUF_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_PIXBUF, GtkCellRendererPixbufClass))
+#define GTK_IS_CELL_RENDERER_PIXBUF(obj)               (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF))
+#define GTK_IS_CELL_RENDERER_PIXBUF_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF))
+
+typedef struct _GtkCellRendererPixbuf GtkCellRendererPixbuf;
+typedef struct _GtkCellRendererPixbufClass GtkCellRendererPixbufClass;
+
+struct _GtkCellRendererPixbuf
+{
+  GtkCellRenderer parent;
+
+  GdkPixbuf *pixbuf;
+};
+
+struct _GtkCellRendererPixbufClass
+{
+  GtkCellRendererClass parent_class;
+};
+
+GtkType          gtk_cell_renderer_pixbuf_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_pixbuf_new      (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_CELL_RENDERER_PIXBUF_H__ */
diff --git a/gtk/gtkcellrenderertext.c b/gtk/gtkcellrenderertext.c
new file mode 100644 (file)
index 0000000..9925f03
--- /dev/null
@@ -0,0 +1,449 @@
+/* gtkcellrenderertext.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gtkcellrenderertext.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+static void gtk_cell_renderer_text_init       (GtkCellRendererText      *celltext);
+static void gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class);
+
+static void gtk_cell_renderer_text_get_param  (GObject                  *object,
+                                              guint                     param_id,
+                                              GValue                   *value,
+                                              GParamSpec               *pspec,
+                                              const gchar              *trailer);
+static void gtk_cell_renderer_text_set_param  (GObject                  *object,
+                                              guint                     param_id,
+                                              GValue                   *value,
+                                              GParamSpec               *pspec,
+                                              const gchar              *trailer);
+static void gtk_cell_renderer_text_get_size   (GtkCellRenderer          *cell,
+                                              GtkWidget                *widget,
+                                              gint                     *width,
+                                              gint                     *height);
+static void gtk_cell_renderer_text_render     (GtkCellRenderer          *cell,
+                                              GdkWindow                *window,
+                                              GtkWidget                *widget,
+                                              GdkRectangle             *background_area,
+                                              GdkRectangle             *cell_area,
+                                              GdkRectangle             *expose_area,
+                                              guint                     flags);
+
+
+enum {
+  PROP_ZERO,
+  PROP_TEXT,
+  PROP_FONT,
+  PROP_BACKGROUND,
+  PROP_BACKGROUND_GDK,
+  PROP_FOREGROUND,
+  PROP_FOREGROUND_GDK,
+  PROP_STRIKETHROUGH,
+  PROP_UNDERLINE,
+  PROP_EDITABLE,
+  PROP_ITALIC,
+  PROP_BOLD
+};
+
+GtkType
+gtk_cell_renderer_text_get_type (void)
+{
+  static GtkType cell_text_type = 0;
+
+  if (!cell_text_type)
+    {
+      static const GTypeInfo cell_text_info =
+      {
+        sizeof (GtkCellRendererTextClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_cell_renderer_text_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkCellRendererText),
+       0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_cell_renderer_text_init,
+      };
+
+      cell_text_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererText", &cell_text_info);
+    }
+
+  return cell_text_type;
+}
+
+static void
+gtk_cell_renderer_text_init (GtkCellRendererText *celltext)
+{
+  celltext->attr_list = pango_attr_list_new ();
+  GTK_CELL_RENDERER (celltext)->xalign = 0.0;
+  GTK_CELL_RENDERER (celltext)->yalign = 0.5;
+  GTK_CELL_RENDERER (celltext)->xpad = 2;
+  GTK_CELL_RENDERER (celltext)->ypad = 2;
+  celltext->underline = FALSE;
+}
+
+static void
+gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+  object_class->get_param = gtk_cell_renderer_text_get_param;
+  object_class->set_param = gtk_cell_renderer_text_set_param;
+
+  cell_class->get_size = gtk_cell_renderer_text_get_size;
+  cell_class->render = gtk_cell_renderer_text_render;
+
+  g_object_class_install_param (object_class,
+                               PROP_TEXT,
+                               g_param_spec_string ("text",
+                                                    _("Text String"),
+                                                    _("The text of the renderer."),
+                                                    "",
+                                                    G_PARAM_READABLE |
+                                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_FONT,
+                               g_param_spec_string ("font",
+                                                    _("Font String"),
+                                                    _("The string of the font."),
+                                                    "",
+                                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_BACKGROUND,
+                               g_param_spec_string ("background",
+                                                    _("Background Color string"),
+                                                    _("The color for the background of the text."),
+                                                    "white",
+                                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_FOREGROUND,
+                               g_param_spec_string ("foreground",
+                                                    _("Foreground Color string"),
+                                                    _("The color for the background of the text."),
+                                                    "black",
+                                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_STRIKETHROUGH,
+                               g_param_spec_boolean ("strikethrough",
+                                                     _("Strikethrough"),
+                                                     _("Draw a line through the text."),
+                                                     FALSE,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_UNDERLINE,
+                               g_param_spec_boolean ("underline",
+                                                     _("Underline"),
+                                                     _("Underline the text."),
+                                                     FALSE,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_EDITABLE,
+                               g_param_spec_boolean ("editable",
+                                                     _("Editable"),
+                                                     _("Make the text editable."),
+                                                     FALSE,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_ITALIC,
+                               g_param_spec_boolean ("italic",
+                                                     _("Italic"),
+                                                     _("Make the text italic."),
+                                                     FALSE,
+                                                     G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_BOLD,
+                               g_param_spec_boolean ("bold",
+                                                     _("Bold"),
+                                                     _("Make the text bold."),
+                                                     FALSE,
+                                                     G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_text_get_param (GObject        *object,
+                                 guint           param_id,
+                                 GValue         *value,
+                                 GParamSpec     *pspec,
+                                 const gchar    *trailer)
+{
+  GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
+  PangoAttrIterator *attr_iter;
+  PangoAttribute *attr;
+
+  switch (param_id)
+    {
+    case PROP_TEXT:
+      g_value_init (value, G_TYPE_STRING);
+      g_value_set_string (value, celltext->text);
+      break;
+    case PROP_STRIKETHROUGH:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      attr_iter = pango_attr_list_get_iterator (celltext->attr_list);
+      attr = pango_attr_iterator_get (attr_iter,
+                                     PANGO_ATTR_STRIKETHROUGH);
+      g_value_set_boolean (value, ((PangoAttrInt*) attr)->value);
+      break;
+    case PROP_UNDERLINE:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, celltext->underline);
+      break;
+    case PROP_EDITABLE:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, celltext->editable);
+      break;
+    default:
+      break;
+    }
+}
+
+
+static void
+gtk_cell_renderer_text_set_param (GObject     *object,
+                                 guint        param_id,
+                                 GValue      *value,
+                                 GParamSpec  *pspec,
+                                 const gchar *trailer)
+{
+  GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
+
+  GdkColor color;
+  PangoFontDescription *font_desc;
+  gchar *string;
+  PangoAttribute *attribute;
+  gchar *font;
+
+  switch (param_id)
+    {
+    case PROP_TEXT:
+      g_free (celltext->text);
+      celltext->text = g_value_dup_string (value);
+      break;
+    case PROP_FONT:
+      font = g_value_get_string (value);
+
+      if (font)
+       {
+         font_desc = pango_font_description_from_string (font);
+         attribute = pango_attr_font_desc_new (font_desc);
+         attribute->start_index = 0;
+         attribute->end_index = G_MAXINT;
+         pango_font_description_free (font_desc);
+         pango_attr_list_change (celltext->attr_list,
+                                 attribute);
+       }
+      break;
+    case PROP_BACKGROUND:
+      string = g_value_get_string (value);
+      if (string && gdk_color_parse (string, &color))
+       {
+         attribute = pango_attr_background_new (color.red,
+                                                color.green,
+                                                color.blue);
+         attribute->start_index = 0;
+         attribute->end_index = G_MAXINT;
+         pango_attr_list_change (celltext->attr_list,
+                                 attribute);
+       }
+      break;
+    case PROP_BACKGROUND_GDK:
+      break;
+    case PROP_FOREGROUND:
+      string = g_value_get_string (value);
+      if (string && gdk_color_parse (string, &color))
+       {
+         attribute = pango_attr_foreground_new (color.red,
+                                                color.green,
+                                                color.blue);
+         attribute->start_index = 0;
+         attribute->end_index = G_MAXINT;
+         pango_attr_list_change (celltext->attr_list,
+                                 attribute);
+       }
+      break;
+    case PROP_FOREGROUND_GDK:
+      break;
+    case PROP_STRIKETHROUGH:
+      attribute = pango_attr_strikethrough_new (g_value_get_boolean (value));
+      attribute->start_index = 0;
+      attribute->end_index = G_MAXINT;
+      pango_attr_list_change (celltext->attr_list,
+                             attribute);
+      break;
+    case PROP_UNDERLINE:
+      celltext->underline = g_value_get_boolean (value);
+      attribute = pango_attr_underline_new (celltext->underline ?
+                                           PANGO_UNDERLINE_SINGLE :
+                                           PANGO_UNDERLINE_NONE);
+      attribute->start_index = 0;
+      attribute->end_index = G_MAXINT;
+      pango_attr_list_change (celltext->attr_list,
+                             attribute);
+      break;
+    case PROP_EDITABLE:
+      break;
+    case PROP_ITALIC:
+      attribute = pango_attr_style_new (g_value_get_boolean (value) ?
+                                       PANGO_STYLE_ITALIC :
+                                       PANGO_STYLE_NORMAL);
+      attribute->start_index = 0;
+      attribute->end_index = G_MAXINT;
+      pango_attr_list_change (celltext->attr_list,
+                             attribute);
+      break;
+    case PROP_BOLD:
+      attribute = pango_attr_weight_new (g_value_get_boolean (value) ?
+                                        PANGO_WEIGHT_BOLD :
+                                        PANGO_WEIGHT_NORMAL);
+      attribute->start_index = 0;
+      attribute->end_index = G_MAXINT;
+      pango_attr_list_change (celltext->attr_list,
+                             attribute);
+      break;
+    default:
+      break;
+    }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_text_new (void)
+{
+  return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_text_get_type ()));
+}
+
+static void
+gtk_cell_renderer_text_get_size (GtkCellRenderer *cell,
+                                GtkWidget       *widget,
+                                gint            *width,
+                                gint            *height)
+{
+  GtkCellRendererText *celltext = (GtkCellRendererText *)cell;
+  PangoRectangle rect;
+  PangoLayout *layout;
+  PangoAttribute *attr;
+  PangoUnderline underline;
+
+  layout = gtk_widget_create_pango_layout (widget, celltext->text);
+  underline = celltext->underline ?
+    PANGO_UNDERLINE_DOUBLE :
+    PANGO_UNDERLINE_NONE;
+
+  attr = pango_attr_underline_new (underline);
+  attr->start_index = 0;
+  attr->end_index = G_MAXINT;
+
+  pango_attr_list_change (celltext->attr_list, attr);
+  pango_layout_set_attributes (layout, celltext->attr_list);
+  pango_layout_set_width (layout, -1);
+  pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+  if (width)
+    *width = GTK_CELL_RENDERER (celltext)->xpad * 2 + rect.width;
+
+  if (height)
+    *height = GTK_CELL_RENDERER (celltext)->ypad * 2 + rect.height;
+
+  g_object_unref (G_OBJECT (layout));
+}
+
+static void
+gtk_cell_renderer_text_render (GtkCellRenderer    *cell,
+                              GdkWindow          *window,
+                              GtkWidget          *widget,
+                              GdkRectangle       *background_area,
+                              GdkRectangle       *cell_area,
+                              GdkRectangle       *expose_area,
+                              guint               flags)
+
+{
+  GtkCellRendererText *celltext = (GtkCellRendererText *) cell;
+  PangoRectangle rect;
+  PangoLayout *layout;
+  PangoAttribute *attr;
+  PangoUnderline underline;
+
+  gint real_xoffset;
+  gint real_yoffset;
+
+  GdkGC *font_gc = NULL;
+
+  if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
+    font_gc = widget->style->fg_gc [GTK_STATE_SELECTED];
+  else
+    font_gc = widget->style->fg_gc [GTK_STATE_NORMAL];
+
+  layout = gtk_widget_create_pango_layout (widget, celltext->text);
+
+  if (celltext->underline)
+    underline = PANGO_UNDERLINE_SINGLE;
+  else
+    underline = PANGO_UNDERLINE_NONE;
+
+  attr = pango_attr_underline_new (underline);
+  attr->start_index = 0;
+  attr->end_index = G_MAXINT;
+  pango_attr_list_change (celltext->attr_list, attr);
+  pango_layout_set_attributes (layout, celltext->attr_list);
+
+  pango_layout_set_width (layout, -1);
+  pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+  if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT)
+    underline = (celltext->underline) ? PANGO_UNDERLINE_DOUBLE:PANGO_UNDERLINE_SINGLE;
+  else
+    underline = (celltext->underline) ? PANGO_UNDERLINE_SINGLE:PANGO_UNDERLINE_NONE;
+
+  attr = pango_attr_underline_new (underline);
+  attr->start_index = 0;
+  attr->end_index = G_MAXINT;
+  pango_attr_list_change (celltext->attr_list, attr);
+  pango_layout_set_attributes (layout, celltext->attr_list);
+
+  gdk_gc_set_clip_rectangle (font_gc, cell_area);
+
+  real_xoffset = cell->xalign * (cell_area->width - rect.width - (2 * cell->xpad));
+  real_xoffset = MAX (real_xoffset, 0) + cell->xpad;
+  real_yoffset = cell->yalign * (cell_area->height - rect.height - (2 * cell->ypad));
+  real_yoffset = MAX (real_yoffset, 0) + cell->ypad;
+
+  gdk_draw_layout (window,
+                  font_gc,
+                  cell_area->x + real_xoffset,
+                  cell_area->y + real_yoffset,
+                  layout);
+
+  g_object_unref (G_OBJECT (layout));
+
+  gdk_gc_set_clip_rectangle (font_gc, NULL);
+}
diff --git a/gtk/gtkcellrenderertext.h b/gtk/gtkcellrenderertext.h
new file mode 100644 (file)
index 0000000..7627054
--- /dev/null
@@ -0,0 +1,65 @@
+/* gtkcellrenderertext.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_TEXT_H__
+#define __GTK_CELL_RENDERER_TEXT_H__
+
+#include <pango/pango.h>
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_TEXT            (gtk_cell_renderer_text_get_type ())
+#define GTK_CELL_RENDERER_TEXT(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererText))
+#define GTK_CELL_RENDERER_TEXT_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererTextClass))
+#define GTK_IS_CELL_RENDERER_TEXT(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT))
+#define GTK_IS_CELL_RENDERER_TEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT))
+
+typedef struct _GtkCellRendererText      GtkCellRendererText;
+typedef struct _GtkCellRendererTextClass GtkCellRendererTextClass;
+
+struct _GtkCellRendererText
+{
+  GtkCellRenderer parent;
+
+  /*< private >*/
+  gchar *text;
+  PangoAttrList *attr_list;
+
+  guint editable  : 1;
+  guint underline : 1;
+};
+
+struct _GtkCellRendererTextClass
+{
+  GtkCellRendererClass parent_class;
+};
+
+GtkType          gtk_cell_renderer_text_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_text_new      (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_TEXT_H__ */
diff --git a/gtk/gtkcellrenderertextpixbuf.c b/gtk/gtkcellrenderertextpixbuf.c
new file mode 100644 (file)
index 0000000..3929a13
--- /dev/null
@@ -0,0 +1,399 @@
+/* gtkcellrenderertextpixbuf.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gtkcellrenderertextpixbuf.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+enum {
+  PROP_ZERO,
+  PROP_PIXBUF_POS,
+  PROP_PIXBUF,
+  PROP_PIXBUF_XALIGN,
+  PROP_PIXBUF_YALIGN,
+  PROP_PIXBUF_XPAD,
+  PROP_PIXBUF_YPAD
+};
+
+
+static void gtk_cell_renderer_text_pixbuf_get_param  (GObject                        *object,
+                                                     guint                           param_id,
+                                                     GValue                         *value,
+                                                     GParamSpec                     *pspec,
+                                                     const gchar                    *trailer);
+static void gtk_cell_renderer_text_pixbuf_set_param  (GObject                        *object,
+                                                     guint                           param_id,
+                                                     GValue                         *value,
+                                                     GParamSpec                     *pspec,
+                                                     const gchar                    *trailer);
+static void gtk_cell_renderer_text_pixbuf_init       (GtkCellRendererTextPixbuf      *celltextpixbuf);
+static void gtk_cell_renderer_text_pixbuf_class_init (GtkCellRendererTextPixbufClass *class);
+static void gtk_cell_renderer_text_pixbuf_get_size   (GtkCellRenderer                *cell,
+                                                     GtkWidget                      *view,
+                                                     gint                           *width,
+                                                     gint                           *height);
+static void gtk_cell_renderer_text_pixbuf_render     (GtkCellRenderer                *cell,
+                                                     GdkWindow                      *window,
+                                                     GtkWidget                      *view,
+                                                     GdkRectangle                   *background_area,
+                                                     GdkRectangle                   *cell_area,
+                                                     GdkRectangle                   *expose_area,
+                                                     guint                           flags);
+
+
+GtkCellRendererTextClass *parent_class = NULL;
+
+
+GtkType
+gtk_cell_renderer_text_pixbuf_get_type (void)
+{
+  static GtkType cell_text_pixbuf_type = 0;
+
+  if (!cell_text_pixbuf_type)
+    {
+      static const GTypeInfo cell_text_pixbuf_info =
+      {
+        sizeof (GtkCellRendererTextPixbufClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_cell_renderer_text_pixbuf_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkCellRendererTextPixbuf),
+       0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_cell_renderer_text_pixbuf_init,
+      };
+
+      cell_text_pixbuf_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT, "GtkCellRendererTextPixbuf", &cell_text_pixbuf_info);
+    }
+
+  return cell_text_pixbuf_type;
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_init (GtkCellRendererTextPixbuf *celltextpixbuf)
+{
+  celltextpixbuf->pixbuf = GTK_CELL_RENDERER_PIXBUF (gtk_cell_renderer_pixbuf_new ());
+  celltextpixbuf->pixbuf_pos = GTK_POS_LEFT;
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_class_init (GtkCellRendererTextPixbufClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+  parent_class = g_type_class_peek_parent (class);
+
+  object_class->get_param = gtk_cell_renderer_text_pixbuf_get_param;
+  object_class->set_param = gtk_cell_renderer_text_pixbuf_set_param;
+
+  cell_class->get_size = gtk_cell_renderer_text_pixbuf_get_size;
+  cell_class->render = gtk_cell_renderer_text_pixbuf_render;
+
+  g_object_class_install_param (object_class,
+                               PROP_PIXBUF_POS,
+                               g_param_spec_int ("pixbufpos",
+                                                 _("Pixbuf location"),
+                                                 _("The relative location of the pixbuf to the text."),
+                                                 GTK_POS_LEFT,
+                                                 GTK_POS_BOTTOM,
+                                                 GTK_POS_LEFT,
+                                                 G_PARAM_READABLE |
+                                                 G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_PIXBUF,
+                               g_param_spec_object ("pixbuf",
+                                                    _("Pixbuf Object"),
+                                                    _("The pixbuf to render."),
+                                                    GDK_TYPE_PIXBUF,
+                                                    G_PARAM_READABLE |
+                                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_PIXBUF_XALIGN,
+                               g_param_spec_float ("pixbuf xalign",
+                                                   _("pixbuf xalign"),
+                                                   _("The x-align of the pixbuf."),
+                                                   0.0,
+                                                   1.0,
+                                                   0.0,
+                                                   G_PARAM_READABLE |
+                                                   G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_PIXBUF_YALIGN,
+                               g_param_spec_float ("pixbuf yalign",
+                                                   _("pixbuf yalign"),
+                                                   _("The y-align of the pixbuf."),
+                                                   0.0,
+                                                   1.0,
+                                                   0.5,
+                                                   G_PARAM_READABLE |
+                                                   G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_PIXBUF_XPAD,
+                               g_param_spec_uint ("pixbuf xpad",
+                                                  _("pixbuf xpad"),
+                                                  _("The xpad of the pixbuf."),
+                                                  0,
+                                                  100,
+                                                  2,
+                                                  G_PARAM_READABLE |
+                                                  G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_PIXBUF_YPAD,
+                               g_param_spec_uint ("pixbuf ypad",
+                                                  _("pixbuf ypad"),
+                                                  _("The ypad of the pixbuf."),
+                                                  0,
+                                                  100,
+                                                  2,
+                                                  G_PARAM_READABLE |
+                                                  G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_get_param (GObject     *object,
+                                        guint        param_id,
+                                        GValue      *value,
+                                        GParamSpec  *pspec,
+                                        const gchar *trailer)
+{
+  GtkCellRendererTextPixbuf *celltextpixbuf = GTK_CELL_RENDERER_TEXT_PIXBUF (object);
+
+  switch (param_id)
+    {
+    case PROP_PIXBUF_POS:
+      g_value_set_int (value, celltextpixbuf->pixbuf_pos);
+      break;
+    case PROP_PIXBUF:
+      g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "pixbuf",
+                         value);
+      break;
+    case PROP_PIXBUF_XALIGN:
+      g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "xalign",
+                         value);
+      break;
+    case PROP_PIXBUF_YALIGN:
+      g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "yalign",
+                         value);
+      break;
+    case PROP_PIXBUF_XPAD:
+      g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "xpad",
+                         value);
+      break;
+    case PROP_PIXBUF_YPAD:
+      g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "ypad",
+                         value);
+      break;
+    default:
+      break;
+    }
+}
+
+
+static void
+gtk_cell_renderer_text_pixbuf_set_param (GObject     *object,
+                                        guint        param_id,
+                                        GValue      *value,
+                                        GParamSpec  *pspec,
+                                        const gchar *trailer)
+{
+  GtkCellRendererTextPixbuf *celltextpixbuf = GTK_CELL_RENDERER_TEXT_PIXBUF (object);
+
+  switch (param_id)
+    {
+    case PROP_PIXBUF:
+      g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "pixbuf",
+                         value);
+      break;
+    case PROP_PIXBUF_POS:
+      celltextpixbuf->pixbuf_pos = g_value_get_int (value);
+      break;
+    case PROP_PIXBUF_XALIGN:
+      g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "xalign",
+                         value);
+      break;
+    case PROP_PIXBUF_YALIGN:
+      g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "yalign",
+                         value);
+      break;
+    case PROP_PIXBUF_XPAD:
+      g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "xpad",
+                         value);
+      break;
+    case PROP_PIXBUF_YPAD:
+      g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+                         "ypad",
+                         value);
+      break;
+    default:
+      break;
+    }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_text_pixbuf_new (void)
+{
+  return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_text_pixbuf_get_type ()));
+}
+
+typedef void (* CellSizeFunc) (GtkCellRenderer    *cell,
+                              GtkWidget          *widget,
+                              gint               *width,
+                              gint               *height);
+typedef void (* CellRenderFunc) (GtkCellRenderer *cell,
+                                GdkWindow       *window,
+                                GtkWidget       *widget,
+                                GdkRectangle    *background_area,
+                                GdkRectangle    *cell_area,
+                                GdkRectangle    *expose_area,
+                                guint            flags);
+
+static void
+gtk_cell_renderer_text_pixbuf_get_size (GtkCellRenderer *cell,
+                                       GtkWidget       *widget,
+                                       gint            *width,
+                                       gint            *height)
+{
+  GtkCellRendererTextPixbuf *celltextpixbuf = (GtkCellRendererTextPixbuf *)cell;
+  gint pixbuf_width;
+  gint pixbuf_height;
+  gint text_width;
+  gint text_height;
+
+  (* GTK_CELL_RENDERER_CLASS (parent_class)->get_size) (cell, widget, &text_width, &text_height);
+  (* GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->get_size) (GTK_CELL_RENDERER (celltextpixbuf->pixbuf),
+                                                                                      widget,
+                                                                                      &pixbuf_width,
+                                                                                      &pixbuf_height);
+  if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+      celltextpixbuf->pixbuf_pos == GTK_POS_RIGHT)
+    {
+      *width = pixbuf_width + text_width;
+      *height = MAX (pixbuf_height, text_height);
+    }
+  else
+    {
+      *width = MAX (pixbuf_width, text_width);
+      *height = pixbuf_height + text_height;
+    }
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_render (GtkCellRenderer *cell,
+                                     GdkWindow       *window,
+                                     GtkWidget       *widget,
+                                     GdkRectangle    *background_area,
+                                     GdkRectangle    *cell_area,
+                                     GdkRectangle    *expose_area,
+                                     guint            flags)
+
+{
+  GtkCellRendererTextPixbuf *celltextpixbuf = (GtkCellRendererTextPixbuf *) cell;
+  CellSizeFunc size_func1, size_func2;
+  CellRenderFunc render_func1, render_func2;
+  GtkCellRenderer *cell1, *cell2;
+  gint tmp_width;
+  gint tmp_height;
+  GdkRectangle real_cell_area;
+
+  if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+      celltextpixbuf->pixbuf_pos == GTK_POS_TOP)
+    {
+      size_func1 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->get_size;
+      render_func1 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->render;
+      cell1 = GTK_CELL_RENDERER (celltextpixbuf->pixbuf);
+
+      size_func2 = GTK_CELL_RENDERER_CLASS (parent_class)->get_size;
+      render_func2 = GTK_CELL_RENDERER_CLASS (parent_class)->render;
+      cell2 = cell;
+    }
+  else
+    {
+      size_func1 = GTK_CELL_RENDERER_CLASS (parent_class)->get_size;
+      render_func1 = GTK_CELL_RENDERER_CLASS (parent_class)->render;
+      cell1 = cell;
+
+      size_func2 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->get_size;
+      render_func2 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->render;
+      cell2 = GTK_CELL_RENDERER (celltextpixbuf->pixbuf);
+    }
+
+  (size_func1) (cell1, widget, &tmp_width, &tmp_height);
+
+  real_cell_area.x = cell_area->x;
+  real_cell_area.y = cell_area->y;
+
+  if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+      celltextpixbuf->pixbuf_pos == GTK_POS_RIGHT)
+    {
+      real_cell_area.width = MIN (tmp_width, cell_area->width);
+      real_cell_area.height = cell_area->height;
+    }
+  else
+    {
+      real_cell_area.height = MIN (tmp_height, cell_area->height);
+      real_cell_area.width = cell_area->width;
+    }
+
+  (render_func1) (cell1,
+                 window,
+                 widget,
+                 background_area,
+                 &real_cell_area,
+                 expose_area,
+                 flags);
+
+  if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+      celltextpixbuf->pixbuf_pos == GTK_POS_RIGHT)
+    {
+      real_cell_area.x = real_cell_area.x + real_cell_area.width;
+      real_cell_area.width = cell_area->width - real_cell_area.width;
+    }
+  else
+    {
+      real_cell_area.y = real_cell_area.y + real_cell_area.height;
+      real_cell_area.height = cell_area->height - real_cell_area.height;
+    }
+
+  (render_func2 ) (cell2,
+                  window,
+                  widget,
+                  background_area,
+                  &real_cell_area,
+                  expose_area,
+                  flags);
+}
diff --git a/gtk/gtkcellrenderertextpixbuf.h b/gtk/gtkcellrenderertextpixbuf.h
new file mode 100644 (file)
index 0000000..fa372b4
--- /dev/null
@@ -0,0 +1,61 @@
+/* gtkcellrenderertextpixbuf.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_TEXT_PIXBUF_H__
+#define __GTK_CELL_RENDERER_TEXT_PIXBUF_H__
+
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkcellrendererpixbuf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF             (gtk_cell_renderer_text_pixbuf_get_type ())
+#define GTK_CELL_RENDERER_TEXT_PIXBUF(obj)             (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF, GtkCellRendererTextPixbuf))
+#define GTK_CELL_RENDERER_TEXT_PIXBUF_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF, GtkCellRendererTextPixbufClass))
+#define GTK_IS_CELL_RENDERER_TEXT_PIXBUF(obj)          (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF))
+#define GTK_IS_CELL_RENDERER_TEXT_PIXBUF_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF))
+
+typedef struct _GtkCellRendererTextPixbuf GtkCellRendererTextPixbuf;
+typedef struct _GtkCellRendererTextPixbufClass GtkCellRendererTextPixbufClass;
+
+struct _GtkCellRendererTextPixbuf
+{
+  GtkCellRendererText parent;
+
+  /*< private >*/
+  GtkCellRendererPixbuf *pixbuf;
+  GtkPositionType pixbuf_pos;
+};
+
+struct _GtkCellRendererTextPixbufClass
+{
+  GtkCellRendererTextClass parent_class;
+};
+
+GtkType          gtk_cell_renderer_text_pixbuf_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_text_pixbuf_new      (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_TEXT_PIXBUF_H__ */
diff --git a/gtk/gtkcellrenderertoggle.c b/gtk/gtkcellrenderertoggle.c
new file mode 100644 (file)
index 0000000..6830604
--- /dev/null
@@ -0,0 +1,321 @@
+/* gtkcellrenderertoggle.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtkcellrenderertoggle.h>
+#include <gtk/gtksignal.h>
+
+#ifndef _
+#define _(x) x
+#endif
+
+
+static void gtk_cell_renderer_toggle_get_param  (GObject                    *object,
+                                                guint                       param_id,
+                                                GValue                     *value,
+                                                GParamSpec                 *pspec,
+                                                const gchar                *trailer);
+static void gtk_cell_renderer_toggle_set_param  (GObject                    *object,
+                                                guint                       param_id,
+                                                GValue                     *value,
+                                                GParamSpec                 *pspec,
+                                                const gchar                *trailer);
+static void gtk_cell_renderer_toggle_init       (GtkCellRendererToggle      *celltext);
+static void gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class);
+static void gtk_cell_renderer_toggle_get_size   (GtkCellRenderer            *cell,
+                                                GtkWidget                  *widget,
+                                                gint                       *width,
+                                                gint                       *height);
+static void gtk_cell_renderer_toggle_render     (GtkCellRenderer            *cell,
+                                                GdkWindow                  *window,
+                                                GtkWidget                  *widget,
+                                                GdkRectangle               *background_area,
+                                                GdkRectangle               *cell_area,
+                                                GdkRectangle               *expose_area,
+                                                guint                       flags);
+static gint gtk_cell_renderer_toggle_event      (GtkCellRenderer            *cell,
+                                                GdkEvent                   *event,
+                                                GtkWidget                  *widget,
+                                                gchar                      *path,
+                                                GdkRectangle               *background_area,
+                                                GdkRectangle               *cell_area,
+                                                guint                       flags);
+
+
+enum {
+  TOGGLED,
+  LAST_SIGNAL
+};
+
+enum {
+  PROP_ZERO,
+  PROP_STATE,
+  PROP_RADIO
+};
+
+
+#define TOGGLE_WIDTH 12
+
+static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_cell_renderer_toggle_get_type (void)
+{
+  static GtkType cell_toggle_type = 0;
+
+  if (!cell_toggle_type)
+    {
+      static const GTypeInfo cell_toggle_info =
+      {
+       sizeof (GtkCellRendererToggleClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       (GClassInitFunc) gtk_cell_renderer_toggle_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkCellRendererToggle),
+       0,              /* n_preallocs */
+       (GInstanceInitFunc) gtk_cell_renderer_toggle_init,
+      };
+
+      cell_toggle_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererToggle", &cell_toggle_info);
+    }
+
+  return cell_toggle_type;
+}
+
+static void
+gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltoggle)
+{
+  celltoggle->state = FALSE;
+  celltoggle->radio = FALSE;
+  GTK_CELL_RENDERER (celltoggle)->xpad = 2;
+  GTK_CELL_RENDERER (celltoggle)->ypad = 2;
+}
+
+static void
+gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+  object_class->get_param = gtk_cell_renderer_toggle_get_param;
+  object_class->set_param = gtk_cell_renderer_toggle_set_param;
+
+  cell_class->get_size = gtk_cell_renderer_toggle_get_size;
+  cell_class->render = gtk_cell_renderer_toggle_render;
+  cell_class->event = gtk_cell_renderer_toggle_event;
+
+  g_object_class_install_param (object_class,
+                               PROP_STATE,
+                               g_param_spec_boolean ("state",
+                                                     _("Toggle state"),
+                                                     _("The toggle-state of the button."),
+                                                     FALSE,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_WRITABLE));
+
+  g_object_class_install_param (object_class,
+                               PROP_RADIO,
+                               g_param_spec_boolean ("radio",
+                                                     _("Radio state"),
+                                                     _("Draw the toggle button as a radio button."),
+                                                     FALSE,
+                                                     G_PARAM_READABLE |
+                                                     G_PARAM_WRITABLE));
+
+
+  toggle_cell_signals[TOGGLED] =
+    gtk_signal_new ("toggled",
+                   GTK_RUN_LAST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkCellRendererToggleClass, toggled),
+                   gtk_marshal_NONE__POINTER,
+                   GTK_TYPE_NONE, 1,
+                   GTK_TYPE_POINTER);
+
+  gtk_object_class_add_signals (GTK_OBJECT_CLASS (object_class), toggle_cell_signals, LAST_SIGNAL);
+}
+
+static void
+gtk_cell_renderer_toggle_get_param (GObject     *object,
+                                   guint        param_id,
+                                   GValue      *value,
+                                   GParamSpec  *pspec,
+                                   const gchar *trailer)
+{
+  GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
+
+  switch (param_id)
+    {
+    case PROP_STATE:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, celltoggle->state);
+      break;
+    case PROP_RADIO:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, celltoggle->radio);
+      break;
+    default:
+      break;
+    }
+}
+
+
+static void
+gtk_cell_renderer_toggle_set_param (GObject     *object,
+                                   guint        param_id,
+                                   GValue      *value,
+                                   GParamSpec  *pspec,
+                                   const gchar *trailer)
+{
+  GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
+
+  switch (param_id)
+    {
+    case PROP_STATE:
+      celltoggle->state = g_value_get_boolean (value);
+      break;
+    case PROP_RADIO:
+      celltoggle->radio = g_value_get_boolean (value);
+      break;
+    default:
+      break;
+    }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_toggle_new (void)
+{
+  return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_toggle_get_type ()));
+}
+
+static void
+gtk_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
+                                  GtkWidget       *widget,
+                                  gint            *width,
+                                  gint            *height)
+{
+  if (width)
+    *width = (gint) cell->xpad * 2 + TOGGLE_WIDTH;
+
+  if (height)
+    *height = (gint) cell->ypad * 2 + TOGGLE_WIDTH;
+}
+
+static void
+gtk_cell_renderer_toggle_render (GtkCellRenderer *cell,
+                                GdkWindow       *window,
+                                GtkWidget       *widget,
+                                GdkRectangle    *background_area,
+                                GdkRectangle    *cell_area,
+                                GdkRectangle    *expose_area,
+                                guint            flags)
+{
+  GtkCellRendererToggle *celltoggle = (GtkCellRendererToggle *) cell;
+  gint width, height;
+  gint real_xoffset, real_yoffset;
+
+  width = MIN (TOGGLE_WIDTH, cell_area->width - cell->xpad * 2);
+  height = MIN (TOGGLE_WIDTH, cell_area->height - cell->ypad * 2);
+
+  if (width <= 0 || height <= 0)
+    return;
+
+  real_xoffset = cell->xalign * (cell_area->width - width - (2 * cell->xpad));
+  real_xoffset = MAX (real_xoffset, 0) + cell->xpad;
+  real_yoffset = cell->yalign * (cell_area->height - height - (2 * cell->ypad));
+  real_yoffset = MAX (real_yoffset, 0) + cell->ypad;
+
+  gdk_gc_set_clip_rectangle (widget->style->black_gc, cell_area);
+
+  if (!celltoggle->radio)
+    {
+      gdk_draw_rectangle (window,
+                         widget->style->black_gc,
+                         FALSE,
+                         cell_area->x + real_xoffset,
+                         cell_area->y + real_yoffset,
+                         width, height);
+      if (celltoggle->state)
+       {
+         gdk_draw_line (window,
+                        widget->style->black_gc,
+                        cell_area->x + real_xoffset,
+                        cell_area->y + real_yoffset,
+                        cell_area->x + real_xoffset + width,
+                        cell_area->y + real_yoffset + height);
+         gdk_draw_line (window,
+                        widget->style->black_gc,
+                        cell_area->x + real_xoffset + width,
+                        cell_area->y + real_yoffset,
+                        cell_area->x + real_xoffset,
+                        cell_area->y + real_yoffset + height);
+       }
+    }
+  else
+    {
+      gdk_draw_arc (window,
+                   widget->style->black_gc,
+                   FALSE,
+                   cell_area->x + real_xoffset,
+                   cell_area->y + real_yoffset,
+                   width,
+                   height,
+                   0, 360*64);
+      if (celltoggle->state)
+       {
+         gdk_draw_arc (window,
+                       widget->style->black_gc,
+                       TRUE,
+                       cell_area->x + real_xoffset + 2,
+                       cell_area->y + real_yoffset + 2,
+                       width - 4,
+                       height - 4,
+                       0, 360*64);
+       }
+    }
+
+
+  gdk_gc_set_clip_rectangle (widget->style->black_gc, NULL);
+}
+
+static gint
+gtk_cell_renderer_toggle_event (GtkCellRenderer *cell,
+                               GdkEvent        *event,
+                               GtkWidget       *widget,
+                               gchar           *path,
+                               GdkRectangle    *background_area,
+                               GdkRectangle    *cell_area,
+                               guint            flags)
+{
+  gtk_signal_emit (GTK_OBJECT (cell), toggle_cell_signals[TOGGLED], path);
+  return TRUE;
+}
+
+void
+gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle,
+                                   gboolean               radio)
+{
+  g_return_if_fail (toggle != NULL);
+  g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
+
+  toggle->radio = radio;
+}
diff --git a/gtk/gtkcellrenderertoggle.h b/gtk/gtkcellrenderertoggle.h
new file mode 100644 (file)
index 0000000..0df6696
--- /dev/null
@@ -0,0 +1,68 @@
+/* gtkcellrenderertoggle.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_TOGGLE_H__
+#define __GTK_CELL_RENDERER_TOGGLE_H__
+
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_TOGGLE                  (gtk_cell_renderer_toggle_get_type ())
+#define GTK_CELL_RENDERER_TOGGLE(obj)                  (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererToggle))
+#define GTK_CELL_RENDERER_TOGGLE_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererToggleClass))
+#define GTK_IS_CELL_RENDERER_TOGGLE(obj)               (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE))
+#define GTK_IS_CELL_RENDERER_TOGGLE_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE))
+
+typedef struct _GtkCellRendererToggle GtkCellRendererToggle;
+typedef struct _GtkCellRendererToggleClass GtkCellRendererToggleClass;
+
+struct _GtkCellRendererToggle
+{
+  GtkCellRenderer parent;
+
+ /*< private >*/
+  guint state : 1;
+  guint radio : 1;
+};
+
+struct _GtkCellRendererToggleClass
+{
+  GtkCellRendererClass parent_class;
+
+  void (* toggled) (GtkCellRendererToggle *celltoggle,
+                   gchar                 *path);
+};
+
+GtkType          gtk_cell_renderer_toggle_get_type  (void);
+GtkCellRenderer *gtk_cell_renderer_toggle_new       (void);
+
+void             gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle,
+                                                    gboolean               radio);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_TOGGLE_H__ */
diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c
new file mode 100644 (file)
index 0000000..5fb4755
--- /dev/null
@@ -0,0 +1,458 @@
+/* gtkliststore.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "gtktreemodel.h"
+#include "gtkliststore.h"
+#include "gtktreedatalist.h"
+
+#define G_SLIST(x) ((GSList *) x)
+
+static void           gtk_list_store_init            (GtkListStore      *list_store);
+static void           gtk_list_store_class_init      (GtkListStoreClass *class);
+static gint           gtk_list_store_get_n_columns   (GtkTreeModel      *tree_model);
+static GtkTreeNode    gtk_list_store_get_node        (GtkTreeModel      *tree_model,
+                                                     GtkTreePath       *path);
+static GtkTreePath   *gtk_list_store_get_path        (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static void           gtk_list_store_node_get_value  (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node,
+                                                     gint               column,
+                                                     GValue            *value);
+static gboolean       gtk_list_store_node_next       (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode       *node);
+static GtkTreeNode    gtk_list_store_node_children   (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static gboolean       gtk_list_store_node_has_child  (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static gint           gtk_list_store_node_n_children (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static GtkTreeNode    gtk_list_store_node_nth_child  (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node,
+                                                     gint               n);
+static GtkTreeNode    gtk_list_store_node_parent     (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+
+
+static GtkTreeModelClass *parent_class = NULL;
+
+
+GtkType
+gtk_list_store_get_type (void)
+{
+  static GtkType list_store_type = 0;
+
+  if (!list_store_type)
+    {
+      static const GTypeInfo list_store_info =
+      {
+       sizeof (GtkListStoreClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_list_store_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkListStore),
+       0,
+        (GInstanceInitFunc) gtk_list_store_init,
+      };
+
+      list_store_type = g_type_register_static (GTK_TYPE_TREE_MODEL, "GtkListStore", &list_store_info);
+    }
+
+  return list_store_type;
+}
+
+static void
+gtk_list_store_class_init (GtkListStoreClass *class)
+{
+  GtkObjectClass *object_class;
+  GtkTreeModelClass *tree_model_class;
+
+  object_class = (GtkObjectClass*) class;
+  tree_model_class = (GtkTreeModelClass *) class;
+
+  parent_class = gtk_type_class (gtk_tree_model_get_type ());
+
+  tree_model_class->get_n_columns = gtk_list_store_get_n_columns;
+  tree_model_class->get_node = gtk_list_store_get_node;
+  tree_model_class->get_path = gtk_list_store_get_path;
+  tree_model_class->node_get_value = gtk_list_store_node_get_value;
+  tree_model_class->node_next = gtk_list_store_node_next;
+  tree_model_class->node_children = gtk_list_store_node_children;
+  tree_model_class->node_has_child = gtk_list_store_node_has_child;
+  tree_model_class->node_n_children = gtk_list_store_node_n_children;
+  tree_model_class->node_nth_child = gtk_list_store_node_nth_child;
+  tree_model_class->node_parent = gtk_list_store_node_parent;
+}
+
+static void
+gtk_list_store_init (GtkListStore *list_store)
+{
+  list_store->root = NULL;
+}
+
+GtkObject *
+gtk_list_store_new (void)
+{
+  return GTK_OBJECT (gtk_type_new (gtk_list_store_get_type ()));
+}
+
+GtkObject *
+gtk_list_store_new_with_types (gint n_columns,
+                              ...)
+{
+  GtkObject *retval;
+  va_list args;
+  gint i;
+
+  g_return_val_if_fail (n_columns > 0, NULL);
+
+  retval = gtk_list_store_new ();
+  gtk_list_store_set_n_columns (GTK_LIST_STORE (retval),
+                             n_columns);
+
+  va_start (args, n_columns);
+  for (i = 0; i < n_columns; i++)
+    gtk_list_store_set_column_type (GTK_LIST_STORE (retval),
+                                   i, va_arg (args, GType));
+
+  va_end (args);
+
+  return retval;
+}
+
+void
+gtk_list_store_set_n_columns (GtkListStore *list_store,
+                             gint          n_columns)
+{
+  GType *new_columns;
+
+  g_return_if_fail (list_store != NULL);
+  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
+
+  if (list_store->n_columns == n_columns)
+    return;
+
+  new_columns = g_new0 (GType, n_columns);
+  if (list_store->column_headers)
+    {
+      /* copy the old header orders over */
+      if (n_columns >= list_store->n_columns)
+       memcpy (new_columns, list_store->column_headers, list_store->n_columns * sizeof (gchar *));
+      else
+       memcpy (new_columns, list_store->column_headers, n_columns * sizeof (GType));
+
+      g_free (list_store->column_headers);
+    }
+
+  list_store->column_headers = new_columns;
+  list_store->n_columns = n_columns;
+}
+
+void
+gtk_list_store_set_column_type (GtkListStore *list_store,
+                               gint          column,
+                               GType         type)
+{
+  g_return_if_fail (list_store != NULL);
+  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
+  g_return_if_fail (column >=0 && column < list_store->n_columns);
+
+  list_store->column_headers[column] = type;
+}
+
+/* Fulfill the GtkTreeModel requirements */
+static gint
+gtk_list_store_get_n_columns (GtkTreeModel *tree_model)
+{
+  g_return_val_if_fail (tree_model != NULL, 0);
+  g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), 0);
+
+  return GTK_LIST_STORE (tree_model)->n_columns;
+}
+
+static GtkTreeNode
+gtk_list_store_get_node (GtkTreeModel *tree_model,
+                        GtkTreePath  *path)
+{
+  g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, NULL);
+
+  return (GtkTreeNode) g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root),
+                                   gtk_tree_path_get_indices (path)[0]);
+}
+
+static GtkTreePath *
+gtk_list_store_get_path (GtkTreeModel *tree_model,
+                        GtkTreeNode   node)
+{
+  GtkTreePath *retval;
+  GSList *list;
+  gint i = 0;
+
+  g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), NULL);
+
+  for (list = G_SLIST (GTK_LIST_STORE (tree_model)->root); list; list = list->next)
+    {
+      i++;
+      if (list == G_SLIST (node))
+       break;
+    }
+  if (list == NULL)
+    return NULL;
+
+  retval = gtk_tree_path_new ();
+  gtk_tree_path_append_index (retval, i);
+
+  return retval;
+}
+
+static void
+gtk_list_store_node_get_value (GtkTreeModel *tree_model,
+                              GtkTreeNode   node,
+                              gint          column,
+                              GValue       *value)
+{
+  GtkTreeDataList *list;
+  gint tmp_column = column;
+
+  g_return_if_fail (tree_model != NULL);
+  g_return_if_fail (GTK_IS_LIST_STORE (tree_model));
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (column < GTK_LIST_STORE (tree_model)->n_columns);
+
+  list = G_SLIST (node)->data;
+
+  while (tmp_column-- > 0 && list)
+    list = list->next;
+
+  g_return_if_fail (list != NULL);
+
+  gtk_tree_data_list_node_to_value (list,
+                                   GTK_LIST_STORE (tree_model)->column_headers[column],
+                                   value);
+}
+
+static gboolean
+gtk_list_store_node_next (GtkTreeModel  *tree_model,
+                         GtkTreeNode   *node)
+{
+  if (node == NULL || *node == NULL)
+    return FALSE;
+
+  *node = (GtkTreeNode *) G_SLIST (*node)->next;
+
+  return (*node != NULL);
+}
+
+static GtkTreeNode
+gtk_list_store_node_children (GtkTreeModel *tree_model,
+                             GtkTreeNode   node)
+{
+  return NULL;
+}
+
+static gboolean
+gtk_list_store_node_has_child (GtkTreeModel *tree_model,
+                              GtkTreeNode   node)
+{
+  return FALSE;
+}
+
+static gint
+gtk_list_store_node_n_children (GtkTreeModel *tree_model,
+                               GtkTreeNode   node)
+{
+  return 0;
+}
+
+static GtkTreeNode
+gtk_list_store_node_nth_child (GtkTreeModel *tree_model,
+                              GtkTreeNode   node,
+                              gint        n)
+{
+  return NULL;
+}
+
+static GtkTreeNode
+gtk_list_store_node_parent (GtkTreeModel *tree_model,
+                           GtkTreeNode   node)
+{
+  return NULL;
+}
+
+/* Public accessors */
+GtkTreeNode *
+gtk_list_store_node_new (void)
+{
+  GtkTreeNode *retval = (GtkTreeNode *) g_slist_alloc ();
+
+  return retval;
+}
+
+/* This is a somewhat inelegant function that does a lot of list
+ * manipulations on it's own.
+ */
+void
+gtk_list_store_node_set_cell (GtkListStore *list_store,
+                             GtkTreeNode  *node,
+                             gint          column,
+                             GValue       *value)
+{
+  GtkTreeDataList *list;
+  GtkTreeDataList *prev;
+
+  g_return_if_fail (list_store != NULL);
+  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (column >= 0 && column < list_store->n_columns);
+
+  prev = list = G_SLIST (node)->data;
+
+  while (list != NULL)
+    {
+      if (column == 0)
+       {
+         gtk_tree_data_list_value_to_node (list, value);
+         return;
+       }
+
+      column--;
+      prev = list;
+      list = list->next;
+    }
+
+  if (G_SLIST (node)->data == NULL)
+    {
+      G_SLIST (node)->data = list = gtk_tree_data_list_alloc ();
+      list->next = NULL;
+    }
+  else
+    {
+      list = prev->next = gtk_tree_data_list_alloc ();
+      list->next = NULL;
+    }
+
+  while (column != 0)
+    {
+      list->next = gtk_tree_data_list_alloc ();
+      list = list->next;
+      list->next = NULL;
+      column --;
+    }
+  gtk_tree_data_list_value_to_node (list, value);
+}
+
+void
+gtk_list_store_node_remove (GtkListStore *list_store,
+                           GtkTreeNode  *node)
+{
+
+}
+
+GtkTreeNode *
+gtk_list_store_node_insert (GtkListStore *list_store,
+                           gint          position,
+                           GtkTreeNode  *node)
+{
+  GSList *list;
+
+  g_return_val_if_fail (list_store != NULL, node);
+  g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+  g_return_val_if_fail (node != NULL, node);
+  g_return_val_if_fail (position < 0, node);
+  g_return_val_if_fail (G_SLIST (node)->next == NULL, node);
+
+  if (position == 0)
+    {
+      gtk_list_store_node_prepend (list_store, node);
+      return node;
+    }
+
+  list = g_slist_nth (G_SLIST (list_store->root), position);
+  if (list)
+    {
+      G_SLIST (node)->next = list->next;
+      list->next = G_SLIST (node)->next;
+    }
+
+  return node;
+}
+
+
+GtkTreeNode *
+gtk_list_store_node_insert_before (GtkListStore *list_store,
+                                  GtkTreeNode  *sibling,
+                                  GtkTreeNode  *node)
+{
+  g_return_val_if_fail (list_store != NULL, node);
+  g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  /* FIXME: This is all wrong.  This is actually insert_after */
+  if (sibling == NULL)
+    return gtk_list_store_node_prepend (list_store, node);
+
+  G_SLIST (node)->next = G_SLIST (sibling)->next;
+  G_SLIST (sibling)->next = G_SLIST (node);
+  return node;
+}
+
+GtkTreeNode *
+gtk_list_store_node_prepend (GtkListStore *list_store,
+                            GtkTreeNode  *node)
+{
+  g_return_val_if_fail (list_store != NULL, node);
+  g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  G_SLIST (node)->next = G_SLIST (list_store->root);
+  list_store->root = node;
+
+  return node;
+}
+
+GtkTreeNode *
+gtk_list_store_node_append (GtkListStore *list_store,
+                           GtkTreeNode  *node)
+{
+  GSList *list;
+
+  g_return_val_if_fail (list_store != NULL, node);
+  g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+  g_return_val_if_fail (node != NULL, node);
+  g_return_val_if_fail (G_SLIST (node)->next == NULL, node);
+
+  list = g_slist_last (G_SLIST (list_store->root));
+  if (list == NULL)
+    list_store->root = node;
+  else
+    list->next = G_SLIST (node);
+
+  return node;
+}
+
+GtkTreeNode *
+gtk_list_store_node_get_root (GtkListStore *list_store)
+{
+  g_return_val_if_fail (list_store != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), NULL);
+
+  return (GtkTreeNode *) list_store->root;
+}
diff --git a/gtk/gtkliststore.h b/gtk/gtkliststore.h
new file mode 100644 (file)
index 0000000..da64388
--- /dev/null
@@ -0,0 +1,87 @@
+/* gtkliststore.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_LIST_STORE_H__
+#define __GTK_LIST_STORE_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_LIST_STORE           (gtk_list_store_get_type ())
+#define GTK_LIST_STORE(obj)           (GTK_CHECK_CAST ((obj), GTK_TYPE_LIST_STORE, GtkListStore))
+#define GTK_LIST_STORE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_LISTSTORE, GtkListStoreClass))
+#define GTK_IS_LIST_STORE(obj)        (GTK_CHECK_TYPE ((obj), GTK_TYPE_LIST_STORE))
+#define GTK_IS_LIST_STORE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_LIST_STORE))
+
+typedef struct _GtkListStore       GtkListStore;
+typedef struct _GtkListStoreClass  GtkListStoreClass;
+
+struct _GtkListStore
+{
+  GtkTreeModel parent;
+
+  /*< private >*/
+  GtkTreeNode *root;
+  gint n_columns;
+  GType *column_headers;
+};
+
+struct _GtkListStoreClass
+{
+  GtkTreeModelClass parent_class;
+};
+
+GtkType      gtk_list_store_get_type           (void);
+GtkObject   *gtk_list_store_new                (void);
+GtkObject   *gtk_list_store_new_with_types     (gint            n_columns,
+                                               ...);
+void         gtk_list_store_set_n_columns      (GtkListStore   *store,
+                                               gint            n_columns);
+void         gtk_list_store_set_column_type    (GtkListStore   *store,
+                                               gint            column,
+                                               GType           type);
+
+GtkTreeNode *gtk_list_store_node_new           (void);
+void         gtk_list_store_node_set_cell      (GtkListStore   *store,
+                                               GtkTreeNode    *node,
+                                               gint            column,
+                                               GValue         *value);
+void         gtk_list_store_node_remove        (GtkListStore   *store,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_list_store_node_insert        (GtkListStore   *store,
+                                               gint            position,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_list_store_node_insert_before (GtkListStore   *store,
+                                               GtkTreeNode    *sibling,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_list_store_node_prepend       (GtkListStore   *store,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_list_store_node_append        (GtkListStore   *store,
+                                               GtkTreeNode    *node);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_LIST_STORE_H__ */
diff --git a/gtk/gtkmodelsimple.c b/gtk/gtkmodelsimple.c
new file mode 100644 (file)
index 0000000..4e6d81d
--- /dev/null
@@ -0,0 +1,430 @@
+/* gtkmodelsimple.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkmodelsimple.h"
+#include "gtkmarshal.h"
+#include "gtksignal.h"
+enum {
+  GET_N_COLUMNS,
+  GET_NODE,
+  GET_PATH,
+  NODE_GET_VALUE,
+  NODE_COPY,
+  NODE_NEXT,
+  NODE_CHILDREN,
+  NODE_HAS_CHILD,
+  NODE_N_CHILDREN,
+  NODE_NTH_CHILD,
+  NODE_PARENT,
+  LAST_SIGNAL
+};
+
+static void         gtk_model_simple_init                 (GtkModelSimple      *model_simple);
+static void         gtk_model_simple_class_init           (GtkModelSimpleClass *class);
+static gint         gtk_real_model_simple_get_n_columns   (GtkTreeModel        *tree_model);
+static GtkTreeNode  gtk_real_model_simple_get_node        (GtkTreeModel        *tree_model,
+                                                          GtkTreePath         *path);
+static GtkTreePath *gtk_real_model_simple_get_path        (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node);
+static void         gtk_real_model_simple_node_get_value  (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node,
+                                                          gint                 column,
+                                                          GValue              *value);
+static gboolean     gtk_real_model_simple_node_next       (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode         *node);
+static GtkTreeNode  gtk_real_model_simple_node_children   (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node);
+static gboolean     gtk_real_model_simple_node_has_child  (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node);
+static gint         gtk_real_model_simple_node_n_children (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node);
+static GtkTreeNode  gtk_real_model_simple_node_nth_child  (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node,
+                                                          gint                 n);
+static GtkTreeNode  gtk_real_model_simple_node_parent     (GtkTreeModel        *tree_model,
+                                                          GtkTreeNode          node);
+
+
+
+static GtkTreeModelClass *parent_class = NULL;
+static guint model_simple_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_model_simple_get_type (void)
+{
+  static GtkType model_simple_type = 0;
+
+  if (!model_simple_type)
+    {
+      static const GTypeInfo model_simple_info =
+      {
+        sizeof (GtkModelSimpleClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_model_simple_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkModelSimple),
+       0,
+        (GInstanceInitFunc) gtk_model_simple_init
+      };
+
+      model_simple_type = g_type_register_static (GTK_TYPE_TREE_MODEL, "GtkModelSimple", &model_simple_info);
+    }
+
+  return model_simple_type;
+}
+
+GtkObject *
+gtk_model_simple_new (void)
+{
+  return GTK_OBJECT (gtk_type_new (GTK_TYPE_MODEL_SIMPLE));
+}
+
+
+typedef gint (*GtkSignal_INT__NONE) (GtkObject * object,
+                                    gpointer user_data);
+void
+gtk_marshal_INT__NONE (GtkObject * object,
+                       GtkSignalFunc func, gpointer func_data, GtkArg * args)
+{
+  GtkSignal_INT__NONE rfunc;
+  gint *return_val;
+  return_val = GTK_RETLOC_INT (args[0]);
+  rfunc = (GtkSignal_INT__NONE) func;
+  *return_val = (*rfunc) (object, func_data);
+}
+
+typedef gpointer (*GtkSignal_POINTER__NONE) (GtkObject * object,
+                                        gpointer user_data);
+void
+gtk_marshal_POINTER__NONE (GtkObject * object,
+                          GtkSignalFunc func, gpointer func_data, GtkArg * args)
+{
+  GtkSignal_POINTER__NONE rfunc;
+  gpointer *return_val;
+  return_val = GTK_RETLOC_POINTER (args[0]);
+  rfunc = (GtkSignal_POINTER__NONE) func;
+  *return_val = (*rfunc) (object, func_data);
+}
+
+typedef gpointer (*GtkSignal_POINTER__POINTER) (GtkObject * object,
+                                               gpointer arg1,
+                                               gpointer user_data);
+void
+gtk_marshal_POINTER__POINTER (GtkObject * object,
+                             GtkSignalFunc func,
+                             gpointer func_data, GtkArg * args)
+{
+  GtkSignal_POINTER__POINTER rfunc;
+  gpointer *return_val;
+  return_val = GTK_RETLOC_POINTER (args[1]);
+  rfunc = (GtkSignal_POINTER__POINTER) func;
+  *return_val = (*rfunc) (object, GTK_VALUE_POINTER (args[0]), func_data);
+}
+
+typedef gpointer (*GtkSignal_POINTER__POINTER_INT) (GtkObject * object,
+                                                   gpointer arg1,
+                                                   gint arg2,
+                                                   gpointer user_data);
+void
+gtk_marshal_POINTER__POINTER_INT (GtkObject * object,
+                                 GtkSignalFunc func,
+                                 gpointer func_data, GtkArg * args)
+{
+  GtkSignal_POINTER__POINTER_INT rfunc;
+  gpointer *return_val;
+  return_val = GTK_RETLOC_POINTER (args[2]);
+  rfunc = (GtkSignal_POINTER__POINTER_INT) func;
+  *return_val = (*rfunc) (object, GTK_VALUE_POINTER (args[0]), GTK_VALUE_INT (args[1]), func_data);
+}
+
+static void
+gtk_model_simple_class_init (GtkModelSimpleClass *class)
+{
+  GtkObjectClass *object_class;
+
+  GtkTreeModelClass *tree_model_class;
+
+  object_class = (GtkObjectClass*) class;
+  tree_model_class = (GtkTreeModelClass*) class;
+  parent_class = g_type_class_peek_parent (class);
+
+
+  model_simple_signals[GET_N_COLUMNS] =
+    gtk_signal_new ("get_n_columns",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_INT__NONE,
+                    GTK_TYPE_INT, 0);
+  model_simple_signals[GET_NODE] =
+    gtk_signal_new ("get_node",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER,
+                    GTK_TYPE_POINTER, 1,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[GET_PATH] =
+    gtk_signal_new ("get_path",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER,
+                    GTK_TYPE_POINTER, 1,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_GET_VALUE] =
+    gtk_signal_new ("node_get_value",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_NONE__POINTER_INT_POINTER,
+                    GTK_TYPE_NONE, 3,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_INT,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_NEXT] =
+    gtk_signal_new ("node_next",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_BOOL__POINTER,
+                    GTK_TYPE_BOOL, 1,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_CHILDREN] =
+    gtk_signal_new ("node_children",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER,
+                    GTK_TYPE_POINTER, 1,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_HAS_CHILD] =
+    gtk_signal_new ("node_has_child",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER,
+                    GTK_TYPE_POINTER, 1,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_N_CHILDREN] =
+    gtk_signal_new ("node_n_children",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER,
+                    GTK_TYPE_POINTER, 1,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_NTH_CHILD] =
+    gtk_signal_new ("node_nth_child",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER_INT,
+                    GTK_TYPE_POINTER, 2,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_POINTER);
+  model_simple_signals[NODE_PARENT] =
+    gtk_signal_new ("node_parent",
+                    GTK_RUN_LAST,
+                    GTK_CLASS_TYPE (object_class),
+                    0,
+                    gtk_marshal_POINTER__POINTER,
+                    GTK_TYPE_POINTER, 1,
+                   GTK_TYPE_POINTER);
+
+
+  tree_model_class->get_n_columns = gtk_real_model_simple_get_n_columns;
+  tree_model_class->get_node = gtk_real_model_simple_get_node;
+  tree_model_class->get_path = gtk_real_model_simple_get_path;
+  tree_model_class->node_get_value = gtk_real_model_simple_node_get_value;
+  tree_model_class->node_next = gtk_real_model_simple_node_next;
+  tree_model_class->node_children = gtk_real_model_simple_node_children;
+  tree_model_class->node_has_child = gtk_real_model_simple_node_has_child;
+  tree_model_class->node_n_children = gtk_real_model_simple_node_n_children;
+  tree_model_class->node_nth_child = gtk_real_model_simple_node_nth_child;
+  tree_model_class->node_parent = gtk_real_model_simple_node_parent;
+
+  gtk_object_class_add_signals (object_class, model_simple_signals, LAST_SIGNAL);
+}
+
+
+static void
+gtk_model_simple_init (GtkModelSimple *model_simple)
+{
+}
+
+static gint
+gtk_real_model_simple_get_n_columns (GtkTreeModel *tree_model)
+{
+  gint retval = 0;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[GET_N_COLUMNS], &retval);
+
+  return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_get_node (GtkTreeModel *tree_model,
+                               GtkTreePath  *path)
+{
+  GtkTreeNode retval;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[GET_NODE], path, &retval);
+
+  return retval;
+}
+
+static GtkTreePath *
+gtk_real_model_simple_get_path (GtkTreeModel *tree_model,
+                               GtkTreeNode   node)
+{
+  GtkTreePath *retval;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[GET_PATH], node, &retval);
+
+  return retval;
+}
+
+static void
+gtk_real_model_simple_node_get_value (GtkTreeModel *tree_model,
+                                     GtkTreeNode   node,
+                                     gint        column,
+                                     GValue     *value)
+{
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_GET_VALUE], node, column, value);
+}
+
+static gboolean
+gtk_real_model_simple_node_next (GtkTreeModel  *tree_model,
+                                GtkTreeNode   *node)
+{
+  gboolean retval = FALSE;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_NEXT], node, &retval);
+
+  return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_node_children (GtkTreeModel *tree_model,
+                                    GtkTreeNode   node)
+{
+  GtkTreeNode retval = NULL;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_CHILDREN], node, &retval);
+
+  return retval;
+}
+
+static gboolean
+gtk_real_model_simple_node_has_child (GtkTreeModel *tree_model,
+                                     GtkTreeNode   node)
+{
+  gboolean retval = FALSE;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_HAS_CHILD], node, &retval);
+
+  return retval;
+}
+
+static gint
+gtk_real_model_simple_node_n_children (GtkTreeModel *tree_model,
+                                      GtkTreeNode   node)
+{
+  gint retval = 0;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_N_CHILDREN], node, &retval);
+
+  return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_node_nth_child (GtkTreeModel *tree_model,
+                                     GtkTreeNode   node,
+                                     gint        n)
+{
+  GtkTreeNode retval = NULL;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_NTH_CHILD], node, n, &retval);
+
+  return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_node_parent (GtkTreeModel *tree_model,
+                                  GtkTreeNode   node)
+{
+  GtkTreeNode retval = NULL;
+
+  gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_PARENT], node, &retval);
+
+  return retval;
+}
+
+/* Public functions */
+void
+gtk_model_simple_node_changed (GtkModelSimple *simple,
+                              GtkTreePath      *path,
+                              GtkTreeNode      *tree_node)
+{
+  g_return_if_fail (simple != NULL);
+  g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+  g_return_if_fail (path != NULL);
+
+  gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_changed", path, tree_node);
+}
+
+void
+gtk_model_simple_node_inserted (GtkModelSimple *simple,
+                               GtkTreePath    *path,
+                               GtkTreeNode    *tree_node)
+{
+  g_return_if_fail (simple != NULL);
+  g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+  g_return_if_fail (path != NULL);
+
+  gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_inserted", path, tree_node);
+}
+
+void
+gtk_model_simple_node_child_toggled (GtkModelSimple *simple,
+                                    GtkTreePath    *path,
+                                    GtkTreeNode    *tree_node)
+{
+  g_return_if_fail (simple != NULL);
+  g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+  g_return_if_fail (path != NULL);
+
+  gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_child_toggled", path, tree_node);
+}
+
+void
+gtk_model_simple_node_deleted (GtkModelSimple *simple,
+                              GtkTreePath    *path,
+                              GtkTreeNode    *tree_node)
+{
+  g_return_if_fail (simple != NULL);
+  g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+  g_return_if_fail (path != NULL);
+
+  gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_deleted", path, tree_node);
+}
diff --git a/gtk/gtkmodelsimple.h b/gtk/gtkmodelsimple.h
new file mode 100644 (file)
index 0000000..bcce4fa
--- /dev/null
@@ -0,0 +1,73 @@
+/* gtkmodelsimple.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_MODEL_SIMPLE_H__
+#define __GTK_MODEL_SIMPLE_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_MODEL_SIMPLE                  (gtk_model_simple_get_type ())
+#define GTK_MODEL_SIMPLE(obj)                  (GTK_CHECK_CAST ((obj), GTK_TYPE_MODEL_SIMPLE, GtkModelSimple))
+#define GTK_MODEL_SIMPLE_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_MODEL_SIMPLE, GtkModelSimpleClass))
+#define GTK_IS_MODEL_SIMPLE(obj)                       (GTK_CHECK_TYPE ((obj), GTK_TYPE_MODEL_SIMPLE))
+#define GTK_IS_MODEL_SIMPLE_CLASS(klass)               (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_MODEL_SIMPLE))
+
+
+typedef struct _GtkModelSimple       GtkModelSimple;
+typedef struct _GtkModelSimpleClass  GtkModelSimpleClass;
+
+struct _GtkModelSimple
+{
+  GtkTreeModel parent;
+};
+
+struct _GtkModelSimpleClass
+{
+  GtkTreeModelClass parent_class;
+};
+
+
+GtkType    gtk_model_simple_get_type           (void);
+GtkObject *gtk_model_simple_new                (void);
+
+void       gtk_model_simple_node_changed       (GtkModelSimple *simple,
+                                               GtkTreePath    *path,
+                                               GtkTreeNode    *tree_node);
+void       gtk_model_simple_node_inserted      (GtkModelSimple *simple,
+                                               GtkTreePath    *path,
+                                               GtkTreeNode    *tree_node);
+void       gtk_model_simple_node_child_toggled (GtkModelSimple *simple,
+                                               GtkTreePath    *path,
+                                               GtkTreeNode    *tree_node);
+void       gtk_model_simple_node_deleted       (GtkModelSimple *simple,
+                                               GtkTreePath    *path,
+                                               GtkTreeNode    *tree_node);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_MODEL_SIMPLE_H__ */
diff --git a/gtk/gtkrbtree.c b/gtk/gtkrbtree.c
new file mode 100644 (file)
index 0000000..92b170d
--- /dev/null
@@ -0,0 +1,994 @@
+/* gtkrbtree.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkrbtree.h"
+
+static void       _gtk_rbnode_validate_allocator (GAllocator *allocator);
+static GtkRBNode *_gtk_rbnode_new                (GtkRBTree  *tree,
+                                                 gint        height);
+static void       _gtk_rbnode_free               (GtkRBNode  *node);
+static void       _gtk_rbnode_rotate_left        (GtkRBTree  *tree,
+                                                 GtkRBNode  *node);
+static void       _gtk_rbnode_rotate_left        (GtkRBTree  *tree,
+                                                 GtkRBNode  *node);
+static void       _gtk_rbtree_insert_fixup       (GtkRBTree  *tree,
+                                                 GtkRBNode  *node);
+static void       _gtk_rbtree_remove_node_fixup  (GtkRBTree  *tree,
+                                                 GtkRBNode  *node);
+static gint       _count_nodes                   (GtkRBTree  *tree,
+                                                 GtkRBNode  *node);
+
+
+/* node allocation
+ */
+struct _GAllocator /* from gmem.c */
+{
+  gchar      *name;
+  guint16     n_preallocs;
+  guint       is_unused : 1;
+  guint       type : 4;
+  GAllocator *last;
+  GMemChunk  *mem_chunk;
+  GtkRBNode  *free_nodes; /* implementation specific */
+};
+
+
+G_LOCK_DEFINE_STATIC (current_allocator);
+static GAllocator *current_allocator = NULL;
+
+/* HOLDS: current_allocator_lock */
+static void
+_gtk_rbnode_validate_allocator (GAllocator *allocator)
+{
+  g_return_if_fail (allocator != NULL);
+  g_return_if_fail (allocator->is_unused == TRUE);
+
+  if (allocator->type != G_ALLOCATOR_NODE)
+    {
+      allocator->type = G_ALLOCATOR_NODE;
+      if (allocator->mem_chunk)
+       {
+         g_mem_chunk_destroy (allocator->mem_chunk);
+         allocator->mem_chunk = NULL;
+       }
+    }
+
+  if (!allocator->mem_chunk)
+    {
+      allocator->mem_chunk = g_mem_chunk_new (allocator->name,
+                                             sizeof (GtkRBNode),
+                                             sizeof (GtkRBNode) * allocator->n_preallocs,
+                                             G_ALLOC_ONLY);
+      allocator->free_nodes = NULL;
+    }
+
+  allocator->is_unused = FALSE;
+}
+
+static GtkRBNode *
+_gtk_rbnode_new (GtkRBTree *tree,
+                gint       height)
+{
+  GtkRBNode *node;
+
+  G_LOCK (current_allocator);
+  if (!current_allocator)
+    {
+      GAllocator *allocator = g_allocator_new ("GTK+ default GtkRBNode allocator",
+                                              128);
+      _gtk_rbnode_validate_allocator (allocator);
+      allocator->last = NULL;
+      current_allocator = allocator;
+    }
+  if (!current_allocator->free_nodes)
+    node = g_chunk_new (GtkRBNode, current_allocator->mem_chunk);
+  else
+    {
+      node = current_allocator->free_nodes;
+      current_allocator->free_nodes = node->left;
+    }
+  G_UNLOCK (current_allocator);
+
+  node->left = tree->nil;
+  node->right = tree->nil;
+  node->parent = tree->nil;
+  node->flags = GTK_RBNODE_RED;
+  node->count = 1;
+  node->children = NULL;
+  node->offset = height;
+  return node;
+}
+
+static void
+_gtk_rbnode_free (GtkRBNode *node)
+{
+  G_LOCK (current_allocator);
+  node->left = current_allocator->free_nodes;
+  current_allocator->free_nodes = node;
+  G_UNLOCK (current_allocator);
+}
+
+static void
+_gtk_rbnode_rotate_left (GtkRBTree *tree,
+                        GtkRBNode *node)
+{
+  gint node_height, right_height;
+  GtkRBNode *right = node->right;
+
+  g_return_if_fail (node != tree->nil);
+
+  node_height = node->offset -
+    (node->left?node->left->offset:0) -
+    (node->right?node->right->offset:0) -
+    (node->children?node->children->root->offset:0);
+  right_height = right->offset -
+    (right->left?right->left->offset:0) -
+    (right->right?right->right->offset:0) -
+    (right->children?right->children->root->offset:0);
+
+  node->right = right->left;
+  if (right->left != tree->nil)
+    right->left->parent = node;
+
+  if (right != tree->nil)
+    right->parent = node->parent;
+  if (node->parent != tree->nil)
+    {
+      if (node == node->parent->left)
+       node->parent->left = right;
+      else
+       node->parent->right = right;
+    } else {
+      tree->root = right;
+    }
+
+  right->left = node;
+  if (node != tree->nil)
+    node->parent = right;
+
+  node->count = 1 + (node->left?node->left->count:0) +
+    (node->right?node->right->count:0);
+  right->count = 1 + (right->left?right->left->count:0) +
+    (right->right?right->right->count:0);
+  node->offset = node_height +
+    (node->left?node->left->offset:0) +
+    (node->right?node->right->offset:0) +
+    (node->children?node->children->root->offset:0);
+  right->offset = right_height +
+    (right->left?right->left->offset:0) +
+    (right->right?right->right->offset:0) +
+    (right->children?right->children->root->offset:0);
+}
+
+static void
+_gtk_rbnode_rotate_right (GtkRBTree *tree,
+                         GtkRBNode *node)
+{
+  gint node_height, left_height;
+  GtkRBNode *left = node->left;
+
+  g_return_if_fail (node != tree->nil);
+
+  node_height = node->offset -
+    (node->left?node->left->offset:0) -
+    (node->right?node->right->offset:0) -
+    (node->children?node->children->root->offset:0);
+  left_height = left->offset -
+    (left->left?left->left->offset:0) -
+    (left->right?left->right->offset:0) -
+    (left->children?left->children->root->offset:0);
+
+  node->left = left->right;
+  if (left->right != tree->nil)
+    left->right->parent = node;
+
+  if (left != tree->nil)
+    left->parent = node->parent;
+  if (node->parent != tree->nil)
+    {
+      if (node == node->parent->right)
+       node->parent->right = left;
+      else
+       node->parent->left = left;
+    }
+  else
+    {
+      tree->root = left;
+    }
+
+  /* link node and left */
+  left->right = node;
+  if (node != tree->nil)
+    node->parent = left;
+
+  node->count = 1 + (node->left?node->left->count:0) +
+    (node->right?node->right->count:0);
+  left->count = 1 + (left->left?left->left->count:0) +
+    (left->right?left->right->count:0);
+  node->offset = node_height +
+    (node->left?node->left->offset:0) +
+    (node->right?node->right->offset:0) +
+    (node->children?node->children->root->offset:0);
+  left->offset = left_height +
+    (left->left?left->left->offset:0) +
+    (left->right?left->right->offset:0) +
+    (left->children?left->children->root->offset:0);
+}
+
+static void
+_gtk_rbtree_insert_fixup (GtkRBTree *tree,
+                         GtkRBNode *node)
+{
+
+  /* check Red-Black properties */
+  while (node != tree->root && GTK_RBNODE_GET_COLOR (node->parent) == GTK_RBNODE_RED)
+    {
+      /* we have a violation */
+      if (node->parent == node->parent->parent->left)
+       {
+         GtkRBNode *y = node->parent->parent->right;
+         if (GTK_RBNODE_GET_COLOR (y) == GTK_RBNODE_RED)
+           {
+                               /* uncle is GTK_RBNODE_RED */
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (y, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+             node = node->parent->parent;
+           }
+         else
+           {
+                               /* uncle is GTK_RBNODE_BLACK */
+             if (node == node->parent->right)
+               {
+                 /* make node a left child */
+                 node = node->parent;
+                 _gtk_rbnode_rotate_left (tree, node);
+               }
+
+                               /* recolor and rotate */
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+             _gtk_rbnode_rotate_right(tree, node->parent->parent);
+           }
+       }
+      else
+       {
+         /* mirror image of above code */
+         GtkRBNode *y = node->parent->parent->left;
+         if (GTK_RBNODE_GET_COLOR (y) == GTK_RBNODE_RED)
+           {
+                               /* uncle is GTK_RBNODE_RED */
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (y, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+             node = node->parent->parent;
+           }
+         else
+           {
+                               /* uncle is GTK_RBNODE_BLACK */
+             if (node == node->parent->left)
+               {
+                 node = node->parent;
+                 _gtk_rbnode_rotate_right (tree, node);
+               }
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+             _gtk_rbnode_rotate_left (tree, node->parent->parent);
+           }
+       }
+    }
+  GTK_RBNODE_SET_COLOR (tree->root, GTK_RBNODE_BLACK);
+}
+
+static void
+_gtk_rbtree_remove_node_fixup (GtkRBTree *tree,
+                              GtkRBNode *node)
+{
+  while (node != tree->root && GTK_RBNODE_GET_COLOR (node) == GTK_RBNODE_BLACK)
+    {
+      if (node == node->parent->left)
+       {
+         GtkRBNode *w = node->parent->right;
+         if (GTK_RBNODE_GET_COLOR (w) == GTK_RBNODE_RED)
+           {
+             GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_RED);
+             _gtk_rbnode_rotate_left (tree, node->parent);
+             w = node->parent->right;
+           }
+         if (GTK_RBNODE_GET_COLOR (w->left) == GTK_RBNODE_BLACK && GTK_RBNODE_GET_COLOR (w->right) == GTK_RBNODE_BLACK)
+           {
+             GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+             node = node->parent;
+           }
+         else
+           {
+             if (GTK_RBNODE_GET_COLOR (w->right) == GTK_RBNODE_BLACK)
+               {
+                 GTK_RBNODE_SET_COLOR (w->left, GTK_RBNODE_BLACK);
+                 GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+                 _gtk_rbnode_rotate_right (tree, w);
+                 w = node->parent->right;
+               }
+             GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_GET_COLOR (node->parent));
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (w->right, GTK_RBNODE_BLACK);
+             _gtk_rbnode_rotate_left (tree, node->parent);
+             node = tree->root;
+           }
+       }
+      else
+       {
+         GtkRBNode *w = node->parent->left;
+         if (GTK_RBNODE_GET_COLOR (w) == GTK_RBNODE_RED)
+           {
+             GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_RED);
+             _gtk_rbnode_rotate_right (tree, node->parent);
+             w = node->parent->left;
+           }
+         if (GTK_RBNODE_GET_COLOR (w->right) == GTK_RBNODE_BLACK && GTK_RBNODE_GET_COLOR (w->left) == GTK_RBNODE_BLACK)
+           {
+             GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+             node = node->parent;
+           }
+         else
+           {
+             if (GTK_RBNODE_GET_COLOR (w->left) == GTK_RBNODE_BLACK)
+               {
+                 GTK_RBNODE_SET_COLOR (w->right, GTK_RBNODE_BLACK);
+                 GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+                 _gtk_rbnode_rotate_left (tree, w);
+                 w = node->parent->left;
+               }
+             GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_GET_COLOR (node->parent));
+             GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+             GTK_RBNODE_SET_COLOR (w->left, GTK_RBNODE_BLACK);
+             _gtk_rbnode_rotate_right (tree, node->parent);
+             node = tree->root;
+           }
+       }
+    }
+  GTK_RBNODE_SET_COLOR (node, GTK_RBNODE_BLACK);
+}
+
+/* Public functions */
+void
+_gtk_rbnode_push_allocator (GAllocator *allocator)
+{
+  G_LOCK (current_allocator);
+  _gtk_rbnode_validate_allocator ( allocator );
+  allocator->last = current_allocator;
+  current_allocator = allocator;
+  G_UNLOCK (current_allocator);
+}
+
+void
+_gtk_rbnode_pop_allocator (void)
+{
+  G_LOCK (current_allocator);
+  if (current_allocator)
+    {
+      GAllocator *allocator;
+
+      allocator = current_allocator;
+      current_allocator = allocator->last;
+      allocator->last = NULL;
+      allocator->is_unused = TRUE;
+    }
+  G_UNLOCK (current_allocator);
+}
+
+GtkRBTree *
+_gtk_rbtree_new (void)
+{
+  GtkRBTree *retval;
+
+  retval = (GtkRBTree *) g_new (GtkRBTree, 1);
+  retval->parent_tree = NULL;
+  retval->parent_node = NULL;
+
+  retval->nil = g_new0 (GtkRBNode, 1);
+  retval->nil->left = NULL;
+  retval->nil->right = NULL;
+  retval->nil->parent = NULL;
+  retval->nil->flags = GTK_RBNODE_BLACK;
+  retval->nil->count = 0;
+  retval->nil->offset = 0;
+
+  retval->root = retval->nil;
+  return retval;
+}
+
+static void
+_gtk_rbtree_free_helper (GtkRBTree  *tree,
+                        GtkRBNode  *node,
+                        gpointer    data)
+{
+  if (node->children)
+    _gtk_rbtree_free (node->children);
+
+  _gtk_rbnode_free (node);
+}
+
+void
+_gtk_rbtree_free (GtkRBTree *tree)
+{
+  _gtk_rbtree_traverse (tree,
+                       tree->root,
+                       G_POST_ORDER,
+                       _gtk_rbtree_free_helper,
+                       NULL);
+
+  if (tree->parent_node &&
+      tree->parent_node->children == tree)
+    tree->parent_node->children = NULL;
+  _gtk_rbnode_free (tree->nil);
+  g_free (tree);
+}
+
+void
+_gtk_rbtree_remove (GtkRBTree *tree)
+{
+  GtkRBTree *tmp_tree;
+  GtkRBNode *tmp_node;
+
+  gint height = tree->root->offset;
+  tmp_tree = tree->parent_tree;
+  tmp_node = tree->parent_node;
+
+  while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+    {
+      tmp_node->offset -= height;
+      tmp_node = tmp_node->parent;
+      if (tmp_node == tmp_tree->nil)
+       {
+         tmp_node = tmp_tree->parent_node;
+         tmp_tree = tmp_tree->parent_tree;
+       }
+    }
+  _gtk_rbtree_free (tree);
+}
+
+
+GtkRBNode *
+_gtk_rbtree_insert_after (GtkRBTree  *tree,
+                         GtkRBNode  *current,
+                         gint        height)
+{
+  GtkRBNode *node;
+  gboolean right = TRUE;
+  GtkRBNode *tmp_node;
+  GtkRBTree *tmp_tree;
+
+  if (current != NULL && current->right != tree->nil)
+    {
+      current = current->right;
+      while (current->left != tree->nil)
+       current = current->left;
+      right = FALSE;
+    }
+
+  /* setup new node */
+  node = _gtk_rbnode_new (tree, height);
+  node->parent = (current?current:tree->nil);
+
+  /* insert node in tree */
+  if (current)
+    {
+      if (right)
+       current->right = node;
+      else
+       current->left = node;
+      tmp_node = node->parent;
+      tmp_tree = tree;
+    }
+  else
+    {
+      tree->root = node;
+      tmp_node = tree->parent_node;
+      tmp_tree = tree->parent_tree;
+    }
+
+  while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+    {
+      /* We only want to propagate the count if we are in the tree we
+       * started in. */
+      if (tmp_tree == tree)
+       tmp_node->count++;
+      tmp_node->offset += height;
+      tmp_node = tmp_node->parent;
+      if (tmp_node == tmp_tree->nil)
+       {
+         tmp_node = tmp_tree->parent_node;
+         tmp_tree = tmp_tree->parent_tree;
+       }
+    }
+  _gtk_rbtree_insert_fixup (tree, node);
+
+  return node;
+}
+
+GtkRBNode *
+_gtk_rbtree_insert_before (GtkRBTree  *tree,
+                          GtkRBNode  *current,
+                          gint        height)
+{
+  GtkRBNode *node;
+  gboolean left = TRUE;
+  GtkRBNode *tmp_node;
+  GtkRBTree *tmp_tree;
+
+  if (current != NULL && current->left != tree->nil)
+    {
+      current = current->left;
+      while (current->right != tree->nil)
+       current = current->right;
+      left = FALSE;
+    }
+
+  /* setup new node */
+  node = _gtk_rbnode_new (tree, height);
+  node->parent = (current?current:tree->nil);
+
+  /* insert node in tree */
+  if (current)
+    {
+      if (left)
+       current->left = node;
+      else
+       current->right = node;
+      tmp_node = node->parent;
+      tmp_tree = tree;
+    }
+  else
+    {
+      tree->root = node;
+      tmp_node = tree->parent_node;
+      tmp_tree = tree->parent_tree;
+    }
+
+  while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+    {
+      /* We only want to propagate the count if we are in the tree we
+       * started in. */
+      if (tmp_tree == tree)
+       tmp_node->count++;
+      tmp_node->offset += height;
+      tmp_node = tmp_node->parent;
+      if (tmp_node == tmp_tree->nil)
+       {
+         tmp_node = tmp_tree->parent_node;
+         tmp_tree = tmp_tree->parent_tree;
+       }
+    }
+  _gtk_rbtree_insert_fixup (tree, node);
+
+  return node;
+}
+
+GtkRBNode *
+_gtk_rbtree_find_count (GtkRBTree *tree,
+                       gint       count)
+{
+  GtkRBNode *node;
+
+  node = tree->root;
+  while (node != tree->nil && (node->left->count + 1 != count))
+    {
+      if (node->left->count >= count)
+       node = node->left;
+      else
+       {
+         count -= (node->left->count + 1);
+         node = node->right;
+       }
+    }
+  if (node == tree->nil)
+    return NULL;
+  return node;
+}
+
+void
+_gtk_rbtree_node_set_height (GtkRBTree *tree,
+                            GtkRBNode *node,
+                            gint       height)
+{
+  gint diff = height - GTK_RBNODE_GET_HEIGHT (node);
+  GtkRBNode *tmp_node = node;
+  GtkRBTree *tmp_tree = tree;
+
+  if (diff == 0)
+    return;
+
+  while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+    {
+      tmp_node->offset += diff;
+      tmp_node = tmp_node->parent;
+      if (tmp_node == tmp_tree->nil)
+       {
+         tmp_node = tmp_tree->parent_node;
+         tmp_tree = tmp_tree->parent_tree;
+       }
+    }
+}
+
+gint
+_gtk_rbtree_node_find_offset (GtkRBTree *tree,
+                             GtkRBNode *node)
+{
+  GtkRBNode *last;
+  gint retval = node->left->offset;
+
+  while (tree && node && node != tree->nil)
+    {
+      last = node;
+      node = node->parent;
+      if (node->right == last)
+       retval += node->left->offset + GTK_RBNODE_GET_HEIGHT (node);
+      if (node == tree->nil)
+       {
+         node = tree->parent_node;
+         tree = tree->parent_tree;
+         if (node)
+           retval += node->left->offset;
+       }
+    }
+  return retval;
+}
+
+gint
+_gtk_rbtree_find_offset (GtkRBTree  *tree,
+                        gint        height,
+                        GtkRBTree **new_tree,
+                        GtkRBNode **new_node)
+{
+  GtkRBNode *tmp_node;
+
+  tmp_node = tree->root;
+  while (tmp_node != tree->nil &&
+        (tmp_node->left->offset > height ||
+         (tmp_node->offset - tmp_node->right->offset) < height))
+    {
+      if (tmp_node->left->offset > height)
+       tmp_node = tmp_node->left;
+      else
+       {
+         height -= (tmp_node->offset - tmp_node->right->offset);
+         tmp_node = tmp_node->right;
+       }
+    }
+  if (tmp_node == tree->nil)
+    {
+      *new_tree = NULL;
+      *new_node = NULL;
+      return 0;
+    }
+  if (tmp_node->children)
+    {
+      if ((tmp_node->offset -
+          tmp_node->right->offset -
+          tmp_node->children->root->offset) > height)
+       {
+         *new_tree = tree;
+         *new_node = tmp_node;
+         return (height - tmp_node->left->offset);
+       }
+      return _gtk_rbtree_find_offset (tmp_node->children,
+                                     height - tmp_node->left->offset -
+                                     (tmp_node->offset -
+                                      tmp_node->left->offset -
+                                      tmp_node->right->offset -
+                                      tmp_node->children->root->offset),
+                                     new_tree,
+                                     new_node);
+    }
+  *new_tree = tree;
+  *new_node = tmp_node;
+  return (height - tmp_node->left->offset);
+}
+
+
+void
+_gtk_rbtree_remove_node (GtkRBTree *tree,
+                        GtkRBNode *node)
+{
+  GtkRBNode *x, *y;
+
+  g_return_if_fail (tree != NULL);
+  g_return_if_fail (node != NULL);
+  /* make sure we're deleting a node that's actually in the tree */
+  for (x = node; x->parent != tree->nil; x = x->parent)
+    ;
+  g_return_if_fail (x == tree->root);
+
+  if (node->left == tree->nil || node->right == tree->nil)
+    {
+      y = node;
+    }
+  else
+    {
+      y = node->right;
+
+      while (y->left != tree->nil)
+       y = y->left;
+    }
+  for (x = y; x != tree->nil; x = x->parent)
+    x->count--;
+  y->count = node->count;
+  /* x is y's only child */
+  if (y->left != tree->nil)
+    x = y->left;
+  else
+    x = y->right;
+
+  /* remove y from the parent chain */
+  x->parent = y->parent;
+  if (y->parent != tree->nil)
+    if (y == y->parent->left)
+      y->parent->left = x;
+    else
+      y->parent->right = x;
+  else
+    tree->root = x;
+
+  if (y != node)
+    node->children = y->children;
+
+  if (GTK_RBNODE_GET_COLOR (y) == GTK_RBNODE_BLACK)
+    _gtk_rbtree_remove_node_fixup (tree, x);
+
+  G_LOCK (current_allocator);
+  y->left = current_allocator->free_nodes;
+  current_allocator->free_nodes = y;
+  G_UNLOCK (current_allocator);
+}
+
+GtkRBNode *
+_gtk_rbtree_next (GtkRBTree *tree,
+                 GtkRBNode *node)
+{
+  g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (node != NULL, NULL);
+
+  /* Case 1: the node's below us. */
+  if (node->right != tree->nil)
+    {
+      node = node->right;
+      while (node->left != tree->nil)
+       node = node->left;
+      return node;
+    }
+
+  /* Case 2: it's an ancestor */
+  while (node->parent != tree->nil)
+    {
+      if (node->parent->right == node)
+       node = node->parent;
+      else
+       return (node->parent);
+    }
+
+  /* Case 3: There is no next node */
+  return NULL;
+}
+
+GtkRBNode *
+_gtk_rbtree_prev (GtkRBTree *tree,
+                 GtkRBNode *node)
+{
+  g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (node != NULL, NULL);
+
+  /* Case 1: the node's below us. */
+  if (node->left != tree->nil)
+    {
+      node = node->left;
+      while (node->right != tree->nil)
+       node = node->right;
+      return node;
+    }
+
+  /* Case 2: it's an ancestor */
+  while (node->parent != tree->nil)
+    {
+      if (node->parent->left == node)
+       node = node->parent;
+      else
+       return (node->parent);
+    }
+
+  /* Case 3: There is no next node */
+  return NULL;
+}
+
+void
+_gtk_rbtree_next_full (GtkRBTree  *tree,
+                      GtkRBNode  *node,
+                      GtkRBTree **new_tree,
+                      GtkRBNode **new_node)
+{
+  g_return_if_fail (tree != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (new_tree != NULL);
+  g_return_if_fail (new_node != NULL);
+
+  if (node->children)
+    {
+      *new_tree = node->children;
+      *new_node = (*new_tree)->root;
+      while ((*new_node)->left != (*new_tree)->nil)
+       *new_node = (*new_node)->left;
+      return;
+    }
+
+  *new_tree = tree;
+  *new_node = _gtk_rbtree_next (tree, node);
+
+  while ((*new_node == NULL) &&
+        (*new_tree != NULL))
+    {
+      *new_node = (*new_tree)->parent_node;
+      *new_tree = (*new_tree)->parent_tree;
+      if (*new_tree)
+       *new_node = _gtk_rbtree_next (*new_tree, *new_node);
+    }
+}
+
+void
+_gtk_rbtree_prev_full (GtkRBTree  *tree,
+                      GtkRBNode  *node,
+                      GtkRBTree **new_tree,
+                      GtkRBNode **new_node)
+{
+  g_return_if_fail (tree != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (new_tree != NULL);
+  g_return_if_fail (new_node != NULL);
+
+  *new_tree = tree;
+  *new_node = _gtk_rbtree_prev (tree, node);
+
+  if (*new_node == NULL)
+    {
+      *new_node = (*new_tree)->parent_node;
+      *new_tree = (*new_tree)->parent_tree;
+    }
+  else
+    {
+      while ((*new_node)->children)
+       {
+         *new_tree = (*new_node)->children;
+         *new_node = (*new_tree)->root;
+         while ((*new_node)->right != (*new_tree)->nil)
+           *new_node = (*new_node)->right;
+       }
+    }
+}
+
+static void
+_gtk_rbtree_traverse_pre_order (GtkRBTree             *tree,
+                               GtkRBNode             *node,
+                               GtkRBTreeTraverseFunc  func,
+                               gpointer               data)
+{
+  if (node == tree->nil)
+    return;
+
+  (* func) (tree, node, data);
+  _gtk_rbtree_traverse_pre_order (tree, node->left, func, data);
+  _gtk_rbtree_traverse_pre_order (tree, node->right, func, data);
+}
+
+static void
+_gtk_rbtree_traverse_post_order (GtkRBTree             *tree,
+                                GtkRBNode             *node,
+                                GtkRBTreeTraverseFunc  func,
+                                gpointer               data)
+{
+  if (node == tree->nil)
+    return;
+
+  _gtk_rbtree_traverse_post_order (tree, node->left, func, data);
+  _gtk_rbtree_traverse_post_order (tree, node->right, func, data);
+  (* func) (tree, node, data);
+}
+
+void
+_gtk_rbtree_traverse (GtkRBTree             *tree,
+                     GtkRBNode             *node,
+                     GTraverseType          order,
+                     GtkRBTreeTraverseFunc  func,
+                     gpointer               data)
+{
+  g_return_if_fail (tree != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (func != NULL);
+  g_return_if_fail (order <= G_LEVEL_ORDER);
+
+  switch (order)
+    {
+    case G_PRE_ORDER:
+      _gtk_rbtree_traverse_pre_order (tree, node, func, data);
+      break;
+    case G_POST_ORDER:
+      _gtk_rbtree_traverse_post_order (tree, node, func, data);
+      break;
+    case G_IN_ORDER:
+    case G_LEVEL_ORDER:
+    default:
+      g_warning ("unsupported traversal order.");
+      break;
+    }
+}
+
+static gint
+_count_nodes (GtkRBTree *tree,
+             GtkRBNode *node)
+{
+  gint res;
+  if (node == tree->nil)
+    return 0;
+
+  res = (_count_nodes (tree, node->left) +
+        _count_nodes (tree, node->right) + 1);
+
+  if (res != node->count)
+    g_print ("Tree failed\n");
+  return res;
+}
+
+void
+_gtk_rbtree_test (GtkRBTree *tree)
+{
+  if ((_count_nodes (tree, tree->root->left) +
+       _count_nodes (tree, tree->root->right) + 1) == tree->root->count)
+    g_print ("Tree passed\n");
+  else
+    g_print ("Tree failed\n");
+
+}
+
+static void
+_gtk_rbtree_test_height_helper (GtkRBTree *tree,
+                               GtkRBNode *node,
+                               gint       height)
+{
+  if (node == tree->nil)
+    return;
+
+  if (node->offset -
+      (node->left?node->left->offset:0) -
+      (node->right?node->right->offset:0) -
+      (node->children?node->children->root->offset:0) != height)
+    g_error ("tree failed\n");
+
+  _gtk_rbtree_test_height_helper (tree, node->left, height);
+  _gtk_rbtree_test_height_helper (tree, node->right, height);
+  if (node->children)
+    _gtk_rbtree_test_height_helper (node->children, node->children->root, height);
+
+}
+
+void
+_gtk_rbtree_test_height (GtkRBTree *tree,
+                        gint       height)
+{
+  _gtk_rbtree_test_height_helper (tree, tree->root, height);
+}
diff --git a/gtk/gtkrbtree.h b/gtk/gtkrbtree.h
new file mode 100644 (file)
index 0000000..9446510
--- /dev/null
@@ -0,0 +1,133 @@
+/* gtkrbtree.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RBTREE_H__
+#define __GTK_RBTREE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <glib.h>
+typedef enum
+{
+  GTK_RBNODE_BLACK = 1 << 0,
+  GTK_RBNODE_RED = 1 << 1,
+  GTK_RBNODE_IS_PARENT = 1 << 2,
+  GTK_RBNODE_IS_SELECTED = 1 << 3,
+  GTK_RBNODE_IS_PRELIT = 1 << 4,
+  GTK_RBNODE_IS_VIEW = 1 << 5
+} GtkRBNodeColor;
+
+typedef struct _GtkRBTree GtkRBTree;
+typedef struct _GtkRBNode GtkRBNode;
+typedef struct _GtkRBTreeView GtkRBTreeView;
+
+typedef void (*GtkRBTreeTraverseFunc) (GtkRBTree  *tree,
+                                    GtkRBNode  *node,
+                                    gpointer  data);
+
+struct _GtkRBTree
+{
+  GtkRBNode *root;
+  GtkRBNode *nil;
+  GtkRBTree *parent_tree;
+  GtkRBNode *parent_node;
+};
+
+struct _GtkRBNode
+{
+  guint flags;
+  GtkRBNode *left;
+  GtkRBNode *right;
+  GtkRBNode *parent;
+  gint count;  /* aggregate number of children we have */
+  gint offset; /* aggregate of the heights of all our children */
+  GtkRBTree *children;
+};
+
+struct _GtkRBNodeView
+{
+  GtkRBNode parent;
+  gint offset;
+  GtkRBTree *children;
+};
+
+#define GTK_RBNODE_GET_COLOR(node)             (node?(((node->flags&GTK_RBNODE_RED)==GTK_RBNODE_RED)?GTK_RBNODE_RED:GTK_RBNODE_BLACK):GTK_RBNODE_BLACK)
+#define GTK_RBNODE_SET_COLOR(node,color)       if((node->flags&color)!=color)node->flags=node->flags^(GTK_RBNODE_RED|GTK_RBNODE_BLACK)
+#define GTK_RBNODE_GET_HEIGHT(node)            (node->offset-(node->left->offset+node->right->offset+(node->children?node->children->root->offset:0)))
+#define GTK_RBNODE_SET_FLAG(node, flag)        G_STMT_START{ (node->flags|=flag); }G_STMT_END
+#define GTK_RBNODE_UNSET_FLAG(node, flag)      G_STMT_START{ (node->flags&=~(flag)); }G_STMT_END
+#define GTK_RBNODE_FLAG_SET(node, flag)        (node?(((node->flags&flag)==flag)?TRUE:FALSE):FALSE)
+
+
+void       _gtk_rbtree_push_allocator   (GAllocator             *allocator);
+void       _gtk_rbtree_pop_allocator    (void);
+GtkRBTree *_gtk_rbtree_new              (void);
+void       _gtk_rbtree_free             (GtkRBTree              *tree);
+void       _gtk_rbtree_remove           (GtkRBTree              *tree);
+void       _gtk_rbtree_destroy          (GtkRBTree              *tree);
+GtkRBNode *_gtk_rbtree_insert_before    (GtkRBTree              *tree,
+                                        GtkRBNode              *node,
+                                        gint                    height);
+GtkRBNode *_gtk_rbtree_insert_after     (GtkRBTree              *tree,
+                                        GtkRBNode              *node,
+                                        gint                    height);
+void       _gtk_rbtree_remove_node      (GtkRBTree              *tree,
+                                        GtkRBNode              *node);
+GtkRBNode *_gtk_rbtree_find_count       (GtkRBTree              *tree,
+                                        gint                    count);
+void       _gtk_rbtree_node_set_height  (GtkRBTree              *tree,
+                                        GtkRBNode              *node,
+                                        gint                    height);
+gint       _gtk_rbtree_node_find_offset (GtkRBTree              *tree,
+                                        GtkRBNode              *node);
+gint       _gtk_rbtree_find_offset      (GtkRBTree              *tree,
+                                        gint                    offset,
+                                        GtkRBTree             **new_tree,
+                                        GtkRBNode             **new_node);
+void       _gtk_rbtree_traverse         (GtkRBTree              *tree,
+                                        GtkRBNode              *node,
+                                        GTraverseType           order,
+                                        GtkRBTreeTraverseFunc   func,
+                                        gpointer                data);
+GtkRBNode *_gtk_rbtree_next             (GtkRBTree              *tree,
+                                        GtkRBNode              *node);
+GtkRBNode *_gtk_rbtree_prev             (GtkRBTree              *tree,
+                                        GtkRBNode              *node);
+void       _gtk_rbtree_next_full        (GtkRBTree              *tree,
+                                        GtkRBNode              *node,
+                                        GtkRBTree             **new_tree,
+                                        GtkRBNode             **new_node);
+void       _gtk_rbtree_prev_full        (GtkRBTree              *tree,
+                                        GtkRBNode              *node,
+                                        GtkRBTree             **new_tree,
+                                        GtkRBNode             **new_node);
+
+
+/* This func just checks the integrity of the tree */
+/* It will go away later. */
+void       _gtk_rbtree_test             (GtkRBTree              *tree);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_RBTREE_H__ */
diff --git a/gtk/gtktreedatalist.c b/gtk/gtktreedatalist.c
new file mode 100644 (file)
index 0000000..47f022f
--- /dev/null
@@ -0,0 +1,159 @@
+/* gtktreedatalist.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreedatalist.h"
+#include "gobject/gvalue.h"
+
+/* node allocation
+ */
+struct _GAllocator /* from gmem.c */
+{
+  gchar           *name;
+  guint16          n_preallocs;
+  guint            is_unused : 1;
+  guint            type : 4;
+  GAllocator      *last;
+  GMemChunk       *mem_chunk;
+  GtkTreeDataList *free_nodes;
+};
+
+
+G_LOCK_DEFINE_STATIC (current_allocator);
+static GAllocator *current_allocator = NULL;
+
+/* HOLDS: current_allocator_lock */
+static void
+gtk_tree_data_list_validate_allocator (GAllocator *allocator)
+{
+  g_return_if_fail (allocator != NULL);
+  g_return_if_fail (allocator->is_unused == TRUE);
+
+  if (allocator->type != G_ALLOCATOR_NODE)
+    {
+      allocator->type = G_ALLOCATOR_NODE;
+      if (allocator->mem_chunk)
+       {
+         g_mem_chunk_destroy (allocator->mem_chunk);
+         allocator->mem_chunk = NULL;
+       }
+    }
+
+  if (!allocator->mem_chunk)
+    {
+      allocator->mem_chunk = g_mem_chunk_new (allocator->name,
+                                             sizeof (GtkTreeDataList),
+                                             sizeof (GtkTreeDataList) * allocator->n_preallocs,
+                                             G_ALLOC_ONLY);
+      allocator->free_nodes = NULL;
+    }
+
+  allocator->is_unused = FALSE;
+}
+
+void
+gtk_tree_data_list_push_allocator (GAllocator *allocator)
+{
+  G_LOCK (current_allocator);
+  gtk_tree_data_list_validate_allocator ( allocator );
+  allocator->last = current_allocator;
+  current_allocator = allocator;
+  G_UNLOCK (current_allocator);
+}
+
+void
+gtk_tree_data_list_pop_allocator (void)
+{
+  G_LOCK (current_allocator);
+  if (current_allocator)
+    {
+      GAllocator *allocator;
+
+      allocator = current_allocator;
+      current_allocator = allocator->last;
+      allocator->last = NULL;
+      allocator->is_unused = TRUE;
+    }
+  G_UNLOCK (current_allocator);
+}
+
+GtkTreeDataList *
+gtk_tree_data_list_alloc (void)
+{
+  GtkTreeDataList *list;
+
+  G_LOCK (current_allocator);
+  if (!current_allocator)
+    {
+       GAllocator *allocator = g_allocator_new ("GTK+ default GtkTreeDataList allocator",
+                                               128);
+       gtk_tree_data_list_validate_allocator (allocator);
+       allocator->last = NULL;
+       current_allocator = allocator;
+    }
+  if (!current_allocator->free_nodes)
+    list = g_chunk_new (GtkTreeDataList, current_allocator->mem_chunk);
+  else
+    {
+      list = current_allocator->free_nodes;
+      current_allocator->free_nodes = list->next;
+    }
+  G_UNLOCK (current_allocator);
+
+  return list;
+}
+
+void
+gtk_tree_data_list_free (GtkTreeDataList *list)
+{
+  G_LOCK (current_allocator);
+  list->next = current_allocator->free_nodes;
+  current_allocator->free_nodes = list;
+  G_UNLOCK (current_allocator);
+}
+
+void
+gtk_tree_data_list_node_to_value (GtkTreeDataList *list,
+                                 GType            type,
+                                 GValue          *value)
+{
+  g_value_init (value, type);
+
+  switch (type)
+    {
+    case G_TYPE_STRING:
+      g_value_set_string (value, (gchar *) list->data.v_pointer);
+      break;
+    }
+}
+
+void
+gtk_tree_data_list_value_to_node (GtkTreeDataList *list,
+                                 GValue          *value)
+{
+  switch (value->g_type)
+    {
+    case G_TYPE_STRING:
+      list->data.v_pointer = g_value_dup_string (value);
+      break;
+    default:
+      g_warning ("Unsupported type (%s) stored.", g_type_name (value->g_type));
+      return;
+    }
+}
+
diff --git a/gtk/gtktreedatalist.h b/gtk/gtktreedatalist.h
new file mode 100644 (file)
index 0000000..823349c
--- /dev/null
@@ -0,0 +1,53 @@
+/* gtktreedatalist.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GTK_TREE_DATA_LIST_H__
+#define __GTK_TREE_DATA_LIST_H__
+
+#include <glib.h>
+#include <gobject/gobject.h>
+
+typedef struct _GtkTreeDataList GtkTreeDataList;
+struct _GtkTreeDataList
+{
+  GtkTreeDataList *next;
+
+  union {
+    gint          v_int;
+    guint         v_uint;
+    gfloat        v_float;
+    gpointer      v_pointer;
+  } data;
+};
+
+/* FIXME: s/gtk/_gtk/g to make internal */
+void             gtk_tree_data_list_push_allocator (GAllocator      *allocator);
+void             gtk_tree_data_list_pop_allocator  (void);
+GtkTreeDataList *gtk_tree_data_list_alloc          (void);
+void             gtk_tree_data_list_free           (GtkTreeDataList *list);
+
+void             gtk_tree_data_list_node_to_value  (GtkTreeDataList   *list,
+                                                   GType              type,
+                                                   GValue            *value);
+void             gtk_tree_data_list_value_to_node  (GtkTreeDataList   *list,
+                                                   GValue            *value);
+
+
+#endif /* __GTK_TREE_DATA_LIST_H__ */
diff --git a/gtk/gtktreemodel.c b/gtk/gtktreemodel.c
new file mode 100644 (file)
index 0000000..2177346
--- /dev/null
@@ -0,0 +1,421 @@
+/* gtktreemodel.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gtktreemodel.h"
+#include "gtksignal.h"
+
+enum {
+  NODE_CHANGED,
+  NODE_INSERTED,
+  NODE_CHILD_TOGGLED,
+  NODE_DELETED,
+  LAST_SIGNAL
+};
+
+struct _GtkTreePath
+{
+  gint depth;
+  gint *indices;
+};
+
+static void gtk_tree_model_init                        (GtkTreeModel            *tree_model);
+static void gtk_tree_model_class_init          (GtkTreeModelClass       *klass);
+
+static GtkObjectClass *parent_class = NULL;
+static guint tree_model_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_tree_model_get_type (void)
+{
+  static GtkType tree_model_type = 0;
+
+  if (!tree_model_type)
+    {
+      static const GTypeInfo tree_model_info =
+      {
+        sizeof (GtkTreeModelClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_tree_model_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkTreeModel),
+       0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_tree_model_init
+      };
+
+      tree_model_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeModel", &tree_model_info);
+    }
+
+  return tree_model_type;
+}
+
+static void
+gtk_tree_model_class_init (GtkTreeModelClass *class)
+{
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*) class;
+
+  parent_class = g_type_class_peek_parent (class);
+
+  tree_model_signals[NODE_CHANGED] =
+    gtk_signal_new ("node_changed",
+                    GTK_RUN_FIRST,
+                    GTK_CLASS_TYPE (object_class),
+                    GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_changed),
+                    gtk_marshal_NONE__POINTER_POINTER,
+                    GTK_TYPE_NONE, 2,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_POINTER);
+  tree_model_signals[NODE_INSERTED] =
+    gtk_signal_new ("node_inserted",
+                    GTK_RUN_FIRST,
+                    GTK_CLASS_TYPE (object_class),
+                    GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_inserted),
+                    gtk_marshal_NONE__POINTER_POINTER,
+                    GTK_TYPE_NONE, 2,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_POINTER);
+  tree_model_signals[NODE_CHILD_TOGGLED] =
+    gtk_signal_new ("node_child_toggled",
+                    GTK_RUN_FIRST,
+                    GTK_CLASS_TYPE (object_class),
+                    GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_child_toggled),
+                    gtk_marshal_NONE__POINTER_POINTER,
+                    GTK_TYPE_NONE, 2,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_POINTER);
+  tree_model_signals[NODE_DELETED] =
+    gtk_signal_new ("node_deleted",
+                    GTK_RUN_FIRST,
+                    GTK_CLASS_TYPE (object_class),
+                    GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_deleted),
+                    gtk_marshal_NONE__POINTER,
+                    GTK_TYPE_NONE, 1,
+                   GTK_TYPE_POINTER);
+
+
+  gtk_object_class_add_signals (object_class, tree_model_signals, LAST_SIGNAL);
+
+
+  class->get_node = NULL;
+  class->node_next = NULL;
+  class->node_children = NULL;
+  class->node_n_children = NULL;
+  class->node_nth_child = NULL;
+  class->node_parent = NULL;
+}
+
+
+static void
+gtk_tree_model_init (GtkTreeModel *tree_model)
+{
+
+}
+
+/* GtkTreePath Operations */
+GtkTreePath *
+gtk_tree_path_new (void)
+{
+  GtkTreePath *retval;
+  retval = (GtkTreePath *) g_new (GtkTreePath, 1);
+  retval->depth = 0;
+  retval->indices = NULL;
+
+  return retval;
+}
+
+GtkTreePath *
+gtk_tree_path_new_from_string (gchar *path)
+{
+  GtkTreePath *retval;
+  gchar *ptr;
+  gint i;
+
+  g_return_val_if_fail (path != NULL, gtk_tree_path_new ());
+
+  retval = gtk_tree_path_new ();
+
+  while (1)
+    {
+      i = strtol (path, &ptr, 10);
+      gtk_tree_path_append_index (retval, i);
+
+      if (*ptr == '\000')
+       break;
+      path = ptr + 1;
+    }
+
+  return retval;
+}
+
+gchar *
+gtk_tree_path_to_string (GtkTreePath *path)
+{
+  gchar *retval, *ptr;
+  gint i;
+
+  if (path->depth == 0)
+    return NULL;
+
+  ptr = retval = (gchar *) g_new0 (char *, path->depth*8);
+  sprintf (retval, "%d", path->indices[0]);
+  while (*ptr != '\000')
+    ptr++;
+
+  for (i = 1; i < path->depth; i++)
+    {
+      sprintf (ptr, ":%d", path->indices[i]);
+      while (*ptr != '\000')
+       ptr++;
+    }
+
+  return retval;
+}
+
+GtkTreePath *
+gtk_tree_path_new_root (void)
+{
+  GtkTreePath *retval;
+
+  retval = gtk_tree_path_new ();
+  gtk_tree_path_append_index (retval, 0);
+
+  return retval;
+}
+
+void
+gtk_tree_path_append_index (GtkTreePath *path,
+                           gint         index)
+{
+  gint *new_indices = g_new (gint, ++path->depth);
+  if (path->indices == NULL)
+    {
+      path->indices = new_indices;
+      path->indices[0] = index;
+      return;
+    }
+
+  memcpy (new_indices, path->indices, (path->depth - 1)*sizeof (gint));
+  g_free (path->indices);
+  path->indices = new_indices;
+  path->indices[path->depth - 1] = index;
+}
+
+void
+gtk_tree_path_prepend_index (GtkTreePath *path,
+                            gint       index)
+{
+  gint *new_indices = g_new (gint, ++path->depth);
+  if (path->indices == NULL)
+    {
+      path->indices = new_indices;
+      path->indices[0] = index;
+      return;
+    }
+  memcpy (new_indices + 1, path->indices, (path->depth - 1)*sizeof (gint));
+  g_free (path->indices);
+  path->indices = new_indices;
+  path->indices[0] = index;
+}
+
+gint
+gtk_tree_path_get_depth (GtkTreePath *path)
+{
+  return path->depth;
+}
+
+gint *
+gtk_tree_path_get_indices (GtkTreePath *path)
+{
+  return path->indices;
+}
+
+void
+gtk_tree_path_free (GtkTreePath *path)
+{
+  g_free (path->indices);
+  g_free (path);
+}
+
+GtkTreePath *
+gtk_tree_path_copy (GtkTreePath *path)
+{
+  GtkTreePath *retval;
+
+  retval = g_new (GtkTreePath, 1);
+  retval->depth = path->depth;
+  retval->indices = g_new (gint, path->depth);
+  memcpy (retval->indices, path->indices, path->depth * sizeof (gint));
+  return retval;
+}
+
+gint
+gtk_tree_path_compare (GtkTreePath  *a,
+                      GtkTreePath  *b)
+{
+  gint p = 0, q = 0;
+
+  g_return_val_if_fail (a != NULL, 0);
+  g_return_val_if_fail (b != NULL, 0);
+  g_return_val_if_fail (a->depth > 0, 0);
+  g_return_val_if_fail (b->depth > 0, 0);
+
+  do
+    {
+      if (a->indices[p] == b->indices[q])
+       continue;
+      return (a->indices[p] < b->indices[q]?1:-1);
+    }
+  while (++p < a->depth && ++q < b->depth);
+  if (a->depth == b->depth)
+    return 0;
+  return (a->depth < b->depth?1:-1);
+}
+
+void
+gtk_tree_path_next (GtkTreePath *path)
+{
+  g_return_if_fail (path != NULL);
+
+  path->indices[path->depth - 1] ++;
+}
+
+gint
+gtk_tree_path_prev (GtkTreePath *path)
+{
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  if (path->indices[path->depth] == 0)
+    return FALSE;
+
+  path->indices[path->depth - 1] --;
+
+  return TRUE;
+}
+
+gint
+gtk_tree_path_up (GtkTreePath *path)
+{
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  if (path->depth == 1)
+    return FALSE;
+
+  path->depth--;
+
+  return TRUE;
+}
+
+void
+gtk_tree_path_down (GtkTreePath *path)
+{
+  g_return_if_fail (path != NULL);
+
+  gtk_tree_path_append_index (path, 0);
+}
+
+gint
+gtk_tree_model_get_n_columns (GtkTreeModel *tree_model)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->get_n_columns != NULL, 0);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->get_n_columns) (tree_model);
+}
+
+/* Node options */
+GtkTreeNode
+gtk_tree_model_get_node (GtkTreeModel *tree_model,
+                        GtkTreePath  *path)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->get_node != NULL, NULL);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->get_node) (tree_model, path);
+}
+
+GtkTreePath *
+gtk_tree_model_get_path (GtkTreeModel *tree_model,
+                        GtkTreeNode   node)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->get_path != NULL, NULL);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->get_path) (tree_model, node);
+}
+
+void
+gtk_tree_model_node_get_value (GtkTreeModel *tree_model,
+                              GtkTreeNode   node,
+                              gint        column,
+                              GValue     *value)
+{
+  g_return_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_get_value != NULL);
+  (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_get_value) (tree_model, node, column, value);
+}
+
+gboolean
+gtk_tree_model_node_next (GtkTreeModel  *tree_model,
+                         GtkTreeNode   *node)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_next != NULL, FALSE);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_next) (tree_model, node);
+}
+
+GtkTreeNode
+gtk_tree_model_node_children (GtkTreeModel *tree_model,
+                             GtkTreeNode   node)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_children != NULL, NULL);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_children) (tree_model, node);
+}
+
+gboolean
+gtk_tree_model_node_has_child (GtkTreeModel *tree_model,
+                              GtkTreeNode   node)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_has_child != NULL, FALSE);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_has_child) (tree_model, node);
+}
+
+gint
+gtk_tree_model_node_n_children (GtkTreeModel *tree_model,
+                               GtkTreeNode   node)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_n_children != NULL, -1);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_n_children) (tree_model, node);
+}
+
+GtkTreeNode
+gtk_tree_model_node_nth_child (GtkTreeModel *tree_model,
+                              GtkTreeNode   node,
+                              gint        n)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_nth_child != NULL, NULL);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_nth_child) (tree_model, node, n);
+}
+
+GtkTreeNode
+gtk_tree_model_node_parent (GtkTreeModel *tree_model,
+                           GtkTreeNode   node)
+{
+  g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_parent != NULL, NULL);
+  return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_parent) (tree_model, node);
+}
+
diff --git a/gtk/gtktreemodel.h b/gtk/gtktreemodel.h
new file mode 100644 (file)
index 0000000..e700349
--- /dev/null
@@ -0,0 +1,144 @@
+/* gtktreemodel.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_MODEL_H__
+#define __GTK_TREE_MODEL_H__
+
+#include <gtk/gtkobject.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_MODEL            (gtk_tree_model_get_type ())
+#define GTK_TREE_MODEL(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModel))
+#define GTK_TREE_MODEL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_MODEL, GtkTreeModelClass))
+#define GTK_IS_TREE_MODEL(obj)        (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_MODEL))
+#define GTK_IS_TREE_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_MODEL))
+#define GTK_TREE_MODEL_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModelClass))
+
+typedef gpointer                  GtkTreeNode;
+typedef struct _GtkTreePath       GtkTreePath;
+typedef struct _GtkTreeModel      GtkTreeModel;
+typedef struct _GtkTreeModelClass GtkTreeModelClass;
+
+struct _GtkTreeModel
+{
+  GtkObject parent;
+};
+
+struct _GtkTreeModelClass
+{
+  GtkObjectClass parent_class;
+
+  /* signals */
+  void       (* node_changed)         (GtkTreeModel *tree_model,
+                                      GtkTreePath  *path,
+                                      GtkTreeNode  *node);
+  void       (* node_inserted)        (GtkTreeModel *tree_model,
+                                      GtkTreePath  *path,
+                                      GtkTreeNode  *node);
+  void       (* node_child_toggled)   (GtkTreeModel *tree_model,
+                                      GtkTreePath  *path,
+                                      GtkTreeNode  *node);
+  void       (* node_deleted)         (GtkTreeModel *tree_model,
+                                      GtkTreePath  *path);
+
+  /* VTable - not signals */
+  gint         (* get_n_columns)   (GtkTreeModel *tree_model);
+  GtkTreeNode  (* get_node)        (GtkTreeModel *tree_model,
+                                   GtkTreePath  *path);
+  GtkTreePath *(* get_path)        (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node);
+  void         (* node_get_value)  (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node,
+                                   gint          column,
+                                   GValue       *value);
+  gboolean     (* node_next)       (GtkTreeModel *tree_model,
+                                   GtkTreeNode  *node);
+  GtkTreeNode  (* node_children)   (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node);
+  gboolean     (* node_has_child)  (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node);
+  gint         (* node_n_children) (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node);
+  GtkTreeNode  (* node_nth_child)  (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node,
+                                   gint          n);
+  GtkTreeNode  (* node_parent)     (GtkTreeModel *tree_model,
+                                   GtkTreeNode   node);
+};
+
+
+/* Basic tree_model operations */
+GtkType        gtk_tree_model_get_type        (void);
+
+/* GtkTreePath Operations */
+GtkTreePath   *gtk_tree_path_new              (void);
+GtkTreePath   *gtk_tree_path_new_from_string  (gchar        *path);
+gchar         *gtk_tree_path_to_string        (GtkTreePath  *path);
+GtkTreePath   *gtk_tree_path_new_root         (void);
+void           gtk_tree_path_append_index     (GtkTreePath  *path,
+                                              gint          index);
+void           gtk_tree_path_prepend_index    (GtkTreePath  *path,
+                                              gint          index);
+gint           gtk_tree_path_get_depth        (GtkTreePath  *path);
+gint          *gtk_tree_path_get_indices      (GtkTreePath  *path);
+void           gtk_tree_path_free             (GtkTreePath  *path);
+GtkTreePath   *gtk_tree_path_copy             (GtkTreePath  *path);
+gint           gtk_tree_path_compare          (GtkTreePath  *a,
+                                              GtkTreePath  *b);
+void           gtk_tree_path_next             (GtkTreePath  *path);
+gint           gtk_tree_path_prev             (GtkTreePath  *path);
+gint           gtk_tree_path_up               (GtkTreePath  *path);
+void           gtk_tree_path_down             (GtkTreePath  *path);
+
+/* Header operations */
+gint           gtk_tree_model_get_n_columns   (GtkTreeModel *tree_model);
+
+/* Node operations */
+GtkTreeNode    gtk_tree_model_get_node        (GtkTreeModel *tree_model,
+                                              GtkTreePath  *path);
+GtkTreePath   *gtk_tree_model_get_path        (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node);
+void           gtk_tree_model_node_get_value  (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node,
+                                              gint          column,
+                                              GValue       *value);
+gboolean       gtk_tree_model_node_next       (GtkTreeModel *tree_model,
+                                              GtkTreeNode  *node);
+GtkTreeNode    gtk_tree_model_node_children   (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node);
+gboolean       gtk_tree_model_node_has_child  (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node);
+gint           gtk_tree_model_node_n_children (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node);
+GtkTreeNode    gtk_tree_model_node_nth_child  (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node,
+                                              gint          n);
+GtkTreeNode    gtk_tree_model_node_parent     (GtkTreeModel *tree_model,
+                                              GtkTreeNode   node);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_MODEL_H__ */
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
new file mode 100644 (file)
index 0000000..0274f32
--- /dev/null
@@ -0,0 +1,191 @@
+/* gtktreeprivate.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_PRIVATE_H__
+#define __GTK_TREE_PRIVATE_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkrbtree.h>
+
+typedef enum {
+  GTK_TREE_VIEW_IS_LIST = 1 << 0,
+  GTK_TREE_VIEW_SHOW_EXPANDERS = 1 << 1,
+  GTK_TREE_VIEW_IN_COLUMN_RESIZE = 1 << 2,
+  GTK_TREE_VIEW_ARROW_PRELIT = 1 << 3,
+  GTK_TREE_VIEW_HEADERS_VISIBLE = 1 << 4,
+  GTK_TREE_VIEW_DRAW_KEYFOCUS = 1 << 5,
+  GTK_TREE_VIEW_MODEL_SETUP = 1 << 6
+} GtkTreeViewFlags;
+
+#define GTK_TREE_VIEW_SET_FLAG(tree_view, flag)   G_STMT_START{ (tree_view->priv->flags|=flag); }G_STMT_END
+#define GTK_TREE_VIEW_UNSET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags&=~(flag)); }G_STMT_END
+#define GTK_TREE_VIEW_FLAG_SET(tree_view, flag)   ((tree_view->priv->flags&flag)==flag)
+#define TREE_VIEW_HEADER_HEIGHT(tree_view)        (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)?tree_view->priv->header_height:0)
+#define TREE_VIEW_COLUMN_SIZE(column)             (CLAMP (column->size, (column->min_width!=-1)?column->min_width:column->size, (column->max_width!=-1)?column->max_width:column->size))
+#define TREE_VIEW_DRAW_EXPANDERS(tree_view)       (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST)&&GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
+
+struct _GtkTreeViewPrivate
+{
+  GtkTreeModel *model;
+
+  guint flags;
+  /* tree information */
+  GtkRBTree *tree;
+
+  gint tab_offset;
+  GtkRBNode *button_pressed_node;
+  GtkRBTree *button_pressed_tree;
+
+  GList *children;
+  gint width;
+  gint height;
+
+  GtkAdjustment *hadjustment;
+  GtkAdjustment *vadjustment;
+
+  GdkWindow *bin_window;
+  GdkWindow *header_window;
+
+  /* Selection stuff */
+  GtkTreePath *anchor;
+  GtkTreePath *cursor;
+
+  /* Column Resizing */
+  GdkCursor *cursor_drag;
+  GdkGC *xor_gc;
+  gint drag_pos;
+  gint x_drag;
+
+  /* Prelight information */
+  GtkRBNode *prelight_node;
+  GtkRBTree *prelight_tree;
+  gint prelight_offset;
+
+  /* Selection information */
+  GtkTreeSelection *selection;
+
+  /* Header information */
+  gint columns;
+  GList *column;
+  gint header_height;
+};
+
+#ifdef __GNUC__
+
+#define TREE_VIEW_INTERNAL_ASSERT(expr, ret)     G_STMT_START{          \
+     if (!(expr))                                                       \
+       {                                                                \
+         g_log (G_LOG_DOMAIN,                                           \
+                G_LOG_LEVEL_CRITICAL,                                   \
+               "file %s: line %d (%s): assertion `%s' failed.\n"       \
+               "There is a disparity between the internal view of the GtkTreeView,\n"    \
+               "and the GtkTreeModel.  This generally means that the model has changed\n"\
+               "without letting the view know.  Any display from now on is likely to\n"  \
+               "be incorrect.\n",                                                        \
+                __FILE__,                                               \
+                __LINE__,                                               \
+                __PRETTY_FUNCTION__,                                    \
+                #expr);                                                 \
+         return ret;                                                    \
+       };                               }G_STMT_END
+
+#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr)     G_STMT_START{             \
+     if (!(expr))                                                       \
+       {                                                                \
+         g_log (G_LOG_DOMAIN,                                           \
+                G_LOG_LEVEL_CRITICAL,                                   \
+               "file %s: line %d (%s): assertion `%s' failed.\n"       \
+               "There is a disparity between the internal view of the GtkTreeView,\n"    \
+               "and the GtkTreeModel.  This generally means that the model has changed\n"\
+               "without letting the view know.  Any display from now on is likely to\n"  \
+               "be incorrect.\n",                                                        \
+                __FILE__,                                               \
+                __LINE__,                                               \
+                __PRETTY_FUNCTION__,                                    \
+                #expr);                                                 \
+         return;                                                        \
+       };                               }G_STMT_END
+
+#else
+
+#define TREE_VIEW_INTERNAL_ASSERT(expr, ret)     G_STMT_START{          \
+     if (!(expr))                                                       \
+       {                                                                \
+         g_log (G_LOG_DOMAIN,                                           \
+                G_LOG_LEVEL_CRITICAL,                                   \
+               "file %s: line %d: assertion `%s' failed.\n"       \
+               "There is a disparity between the internal view of the GtkTreeView,\n"    \
+               "and the GtkTreeModel.  This generally means that the model has changed\n"\
+               "without letting the view know.  Any display from now on is likely to\n"  \
+               "be incorrect.\n",                                                        \
+                __FILE__,                                               \
+                __LINE__,                                               \
+                #expr);                                                 \
+         return ret;                                                    \
+       };                               }G_STMT_END
+
+#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr)     G_STMT_START{          \
+     if (!(expr))                                                       \
+       {                                                                \
+         g_log (G_LOG_DOMAIN,                                           \
+                G_LOG_LEVEL_CRITICAL,                                   \
+               "file %s: line %d: assertion '%s' failed.\n"            \
+               "There is a disparity between the internal view of the GtkTreeView,\n"    \
+               "and the GtkTreeModel.  This generally means that the model has changed\n"\
+               "without letting the view know.  Any display from now on is likely to\n"  \
+               "be incorrect.\n",                                                        \
+                __FILE__,                                               \
+                __LINE__,                                               \
+                #expr);                                                 \
+         return;                                                        \
+       };                               }G_STMT_END
+#endif
+
+/* functions that shouldn't be exported */
+void         _gtk_tree_selection_internal_select_node (GtkTreeSelection  *selection,
+                                                      GtkRBNode         *node,
+                                                      GtkRBTree         *tree,
+                                                      GtkTreePath       *path,
+                                                      GdkModifierType    state);
+gboolean     _gtk_tree_view_find_node                 (GtkTreeView       *tree_view,
+                                                      GtkTreePath       *path,
+                                                      GtkRBTree        **tree,
+                                                      GtkRBNode        **node);
+GtkTreePath *_gtk_tree_view_find_path                 (GtkTreeView       *tree_view,
+                                                      GtkRBTree         *tree,
+                                                      GtkRBNode         *node);
+void         _gtk_tree_view_set_size                  (GtkTreeView       *tree_view,
+                                                      gint               width,
+                                                      gint               height);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_PRIVATE_H__ */
+
diff --git a/gtk/gtktreeselection.c b/gtk/gtktreeselection.c
new file mode 100644 (file)
index 0000000..73d84f0
--- /dev/null
@@ -0,0 +1,702 @@
+/* gtktreeselection.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreeselection.h"
+#include "gtktreeprivate.h"
+#include "gtkrbtree.h"
+#include "gtksignal.h"
+
+static void     gtk_tree_selection_init           (GtkTreeSelection       *selection);
+static void     gtk_tree_selection_class_init     (GtkTreeSelectionClass  *class);
+
+enum {
+  ROW_SELECTED,
+  ROW_UNSELECTED,
+  LAST_SIGNAL
+};
+
+static GtkObjectClass *parent_class = NULL;
+static guint tree_selection_signals[LAST_SIGNAL] = { 0 };
+
+static void
+gtk_tree_selection_real_select_node (GtkTreeSelection *selection, GtkRBTree *tree, GtkRBNode *node, gboolean select)
+{
+  gboolean selected = FALSE;
+  GtkTreePath *path = NULL;
+
+  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) != select)
+    {
+      path = _gtk_tree_view_find_path (selection->tree_view, tree, node);
+      if (selection->user_func)
+       {
+         if ((*selection->user_func) (selection, selection->tree_view->priv->model, path, selection->user_data))
+           selected = TRUE;
+       }
+      else
+       selected = TRUE;
+    }
+  if (selected == TRUE)
+    {
+      GtkTreeNode tree_node;
+      tree_node = gtk_tree_model_get_node (selection->tree_view->priv->model, path);
+
+      node->flags ^= GTK_RBNODE_IS_SELECTED;
+      if (select)
+       gtk_signal_emit (GTK_OBJECT (selection), tree_selection_signals[ROW_SELECTED], selection->tree_view->priv->model, tree_node);
+      else
+       gtk_signal_emit (GTK_OBJECT (selection), tree_selection_signals[ROW_UNSELECTED], selection->tree_view->priv->model, tree_node);
+      gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+    }
+}
+
+GtkType
+gtk_tree_selection_get_type (void)
+{
+  static GtkType selection_type = 0;
+
+  if (!selection_type)
+    {
+      static const GTypeInfo selection_info =
+      {
+        sizeof (GtkTreeSelectionClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_tree_selection_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkTreeSelection),
+       0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_tree_selection_init
+      };
+
+      selection_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeSelection", &selection_info);
+    }
+
+  return selection_type;
+}
+
+static void
+gtk_tree_selection_class_init (GtkTreeSelectionClass *class)
+{
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*) class;
+  parent_class = g_type_class_peek_parent (class);
+
+  tree_selection_signals[ROW_SELECTED] =
+    gtk_signal_new ("row_selected",
+                   GTK_RUN_FIRST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkTreeSelectionClass, row_selected),
+                   gtk_marshal_NONE__POINTER_POINTER,
+                   GTK_TYPE_NONE, 2,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_POINTER);
+
+  tree_selection_signals[ROW_UNSELECTED] =
+    gtk_signal_new ("row_unselected",
+                   GTK_RUN_FIRST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkTreeSelectionClass, row_unselected),
+                   gtk_marshal_NONE__POINTER_POINTER,
+                   GTK_TYPE_NONE, 2,
+                   GTK_TYPE_POINTER,
+                   GTK_TYPE_POINTER);
+
+  gtk_object_class_add_signals (object_class, tree_selection_signals, LAST_SIGNAL);
+
+  class->row_selected = NULL;
+  class->row_unselected = NULL;
+}
+
+static void
+gtk_tree_selection_init (GtkTreeSelection *selection)
+{
+  selection->type = GTK_TREE_SELECTION_MULTI;
+  selection->user_func = NULL;
+  selection->user_data = NULL;
+  selection->user_func = NULL;
+  selection->tree_view = NULL;
+}
+
+GtkObject *
+gtk_tree_selection_new (void)
+{
+  GtkObject *selection;
+
+  selection = GTK_OBJECT (gtk_type_new (GTK_TYPE_TREE_SELECTION));
+
+  return selection;
+}
+
+GtkObject *
+gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view)
+{
+  GtkObject *selection;
+
+  g_return_val_if_fail (tree_view != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+  selection = gtk_tree_selection_new ();
+  gtk_tree_selection_set_tree_view (GTK_TREE_SELECTION (selection), tree_view);
+
+  return selection;
+}
+
+void
+gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
+                                 GtkTreeView      *tree_view)
+{
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  if (tree_view != NULL)
+    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  selection->tree_view = tree_view;
+  tree_view->priv->selection = selection;
+}
+
+void
+gtk_tree_selection_set_type (GtkTreeSelection     *selection,
+                            GtkTreeSelectionType  type)
+{
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+
+  if (selection->type == type)
+    return;
+
+  if (type == GTK_TREE_SELECTION_SINGLE)
+    {
+      GtkRBTree *tree = NULL;
+      GtkRBNode *node = NULL;
+      gint selected = FALSE;
+
+      if (selection->tree_view->priv->anchor)
+       {
+         _gtk_tree_view_find_node (selection->tree_view,
+                                   selection->tree_view->priv->anchor,
+                                   &tree,
+                                   &node);
+
+         if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+           selected = TRUE;
+       }
+      gtk_tree_selection_unselect_all (selection);
+      if (node && selected)
+       GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SELECTED);
+    }
+  selection->type = type;
+}
+
+void
+gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
+                                       GtkTreeSelectionFunc  func,
+                                       gpointer            data)
+{
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (func != NULL);
+
+  selection->user_func = func;
+  selection->user_data = data;
+}
+
+gpointer
+gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
+{
+  g_return_val_if_fail (selection != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
+
+  return selection->user_data;
+}
+
+GtkTreeNode *
+gtk_tree_selection_get_selected (GtkTreeSelection *selection)
+{
+  GtkTreeNode *retval;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+
+  g_return_val_if_fail (selection != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
+
+  if (selection->tree_view->priv->anchor == NULL)
+    return NULL;
+
+  g_return_val_if_fail (selection->tree_view != NULL, NULL);
+  g_return_val_if_fail (selection->tree_view->priv->model != NULL, NULL);
+
+  if (!_gtk_tree_view_find_node (selection->tree_view,
+                               selection->tree_view->priv->anchor,
+                               &tree,
+                               &node) &&
+      ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+    /* We don't want to return the anchor if it isn't actually selected.
+     */
+
+      return NULL;
+
+  retval = gtk_tree_model_get_node (selection->tree_view->priv->model,
+                                   selection->tree_view->priv->anchor);
+  return retval;
+}
+
+void
+gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
+                                    GtkTreeSelectionForeachFunc  func,
+                                    gpointer                     data)
+{
+  GtkTreePath *path;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+  GtkTreeNode tree_node;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+  g_return_if_fail (selection->tree_view->priv->model != NULL);
+
+  if (func == NULL ||
+      selection->tree_view->priv->tree == NULL ||
+      selection->tree_view->priv->tree->root == NULL)
+    return;
+
+  tree = selection->tree_view->priv->tree;
+  node = selection->tree_view->priv->tree->root;
+
+  while (node->left != tree->nil)
+    node = node->left;
+
+  /* find the node internally */
+  path = gtk_tree_path_new_root ();
+  tree_node = gtk_tree_model_get_node (selection->tree_view->priv->model, path);
+  gtk_tree_path_free (path);
+
+  do
+    {
+      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+       (* func) (selection->tree_view->priv->model, tree_node, data);
+      if (node->children)
+       {
+         tree = node->children;
+         node = tree->root;
+         while (node->left != tree->nil)
+           node = node->left;
+         tree_node = gtk_tree_model_node_children (selection->tree_view->priv->model, tree_node);
+
+         /* Sanity Check! */
+         TREE_VIEW_INTERNAL_ASSERT_VOID (tree_node != NULL);
+       }
+      else
+       {
+         gboolean done = FALSE;
+         do
+           {
+             node = _gtk_rbtree_next (tree, node);
+             if (node != NULL)
+               {
+                 gtk_tree_model_node_next (selection->tree_view->priv->model, &tree_node);
+                 done = TRUE;
+
+                 /* Sanity Check! */
+                 TREE_VIEW_INTERNAL_ASSERT_VOID (tree_node != NULL);
+               }
+             else
+               {
+                 node = tree->parent_node;
+                 tree = tree->parent_tree;
+                 if (tree == NULL)
+                   /* we've run out of tree */
+                   /* We're done with this function */
+                   return;
+                 tree_node = gtk_tree_model_node_parent (selection->tree_view->priv->model, tree_node);
+
+                 /* Sanity check */
+                 TREE_VIEW_INTERNAL_ASSERT_VOID (tree_node != NULL);
+               }
+           }
+         while (!done);
+       }
+    }
+  while (TRUE);
+}
+
+void
+gtk_tree_selection_select_path (GtkTreeSelection *selection,
+                               GtkTreePath      *path)
+{
+  GtkRBNode *node;
+  GtkRBTree *tree;
+  GdkModifierType state = 0;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+  g_return_if_fail (path != NULL);
+
+  _gtk_tree_view_find_node (selection->tree_view,
+                           path,
+                           &tree,
+                           &node);
+
+  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+    return;
+
+  if (selection->type == GTK_TREE_SELECTION_MULTI)
+    state = GDK_CONTROL_MASK;
+
+  _gtk_tree_selection_internal_select_node (selection,
+                                           node,
+                                           tree,
+                                           path,
+                                           state);
+}
+
+void
+gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
+                                 GtkTreePath      *path)
+{
+  GtkRBNode *node;
+  GtkRBTree *tree;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+  g_return_if_fail (path != NULL);
+
+  _gtk_tree_view_find_node (selection->tree_view,
+                           path,
+                           &tree,
+                           &node);
+
+  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+    return;
+
+  _gtk_tree_selection_internal_select_node (selection,
+                                           node,
+                                           tree,
+                                           path,
+                                           GDK_CONTROL_MASK);
+}
+
+void
+gtk_tree_selection_select_node (GtkTreeSelection *selection,
+                               GtkTreeNode      *tree_node)
+{
+  GtkTreePath *path;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+  g_return_if_fail (selection->tree_view->priv->model != NULL);
+
+  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
+                                 tree_node);
+
+  if (path == NULL)
+    return;
+
+  gtk_tree_selection_select_path (selection, path);
+  gtk_tree_path_free (path);
+}
+
+
+void
+gtk_tree_selection_unselect_node (GtkTreeSelection *selection,
+                                 GtkTreeNode      *tree_node)
+{
+  GtkTreePath *path;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+
+  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
+                                 tree_node);
+
+  if (path == NULL)
+    return;
+
+  gtk_tree_selection_select_path (selection, path);
+  gtk_tree_path_free (path);
+}
+
+/* Wish I was in python, right now... */
+struct _TempTuple {
+  GtkTreeSelection *selection;
+  gint dirty;
+};
+
+static void
+select_all_helper (GtkRBTree  *tree,
+                  GtkRBNode  *node,
+                  gpointer  data)
+{
+  struct _TempTuple *tuple = data;
+
+  if (node->children)
+    _gtk_rbtree_traverse (node->children,
+                         node->children->root,
+                         G_PRE_ORDER,
+                         select_all_helper,
+                         data);
+  if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+    {
+      gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE);
+      tuple->dirty = TRUE;
+    }
+}
+
+void
+gtk_tree_selection_select_all (GtkTreeSelection *selection)
+{
+  struct _TempTuple *tuple;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+  g_return_if_fail (selection->tree_view->priv->tree != NULL);
+
+  if (selection->type == GTK_TREE_SELECTION_SINGLE)
+    {
+      GtkRBNode *node;
+      node = selection->tree_view->priv->tree->root;
+
+      while (node->right != selection->tree_view->priv->tree->nil)
+       node = node->right;
+      return;
+    }
+
+  tuple = g_new (struct _TempTuple, 1);
+  tuple->selection = selection;
+  tuple->dirty = FALSE;
+
+  _gtk_rbtree_traverse (selection->tree_view->priv->tree,
+                       selection->tree_view->priv->tree->root,
+                       G_PRE_ORDER,
+                       select_all_helper,
+                       tuple);
+  if (tuple->dirty)
+    gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+  g_free (tuple);
+}
+
+static void
+unselect_all_helper (GtkRBTree  *tree,
+                    GtkRBNode  *node,
+                    gpointer  data)
+{
+  struct _TempTuple *tuple = data;
+
+  if (node->children)
+    _gtk_rbtree_traverse (node->children,
+                         node->children->root,
+                         G_PRE_ORDER,
+                         unselect_all_helper,
+                         data);
+  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+    {
+      gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE);
+      tuple->dirty = TRUE;
+    }
+}
+
+void
+gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
+{
+  struct _TempTuple *tuple;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+  if (selection->tree_view->priv->tree == NULL)
+    return;
+
+  if (selection->type == GTK_TREE_SELECTION_SINGLE)
+    {
+      GtkRBTree *tree = NULL;
+      GtkRBNode *node = NULL;
+      if (selection->tree_view->priv->anchor == NULL)
+       return;
+
+      _gtk_tree_view_find_node (selection->tree_view,
+                               selection->tree_view->priv->anchor,
+                               &tree,
+                               &node);
+      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+       gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
+      return;
+    }
+
+  tuple = g_new (struct _TempTuple, 1);
+  tuple->selection = selection;
+  tuple->dirty = FALSE;
+
+  _gtk_rbtree_traverse (selection->tree_view->priv->tree,
+                       selection->tree_view->priv->tree->root,
+                       G_PRE_ORDER,
+                       unselect_all_helper,
+                       tuple);
+  if (tuple->dirty)
+    gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+  g_free (tuple);
+}
+
+void
+gtk_tree_selection_select_range (GtkTreeSelection *selection,
+                                GtkTreePath      *start_path,
+                                GtkTreePath      *end_path)
+{
+  GtkRBNode *start_node, *end_node;
+  GtkRBTree *start_tree, *end_tree;
+
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+  g_return_if_fail (selection->tree_view != NULL);
+
+  switch (gtk_tree_path_compare (start_path, end_path))
+    {
+    case -1:
+      _gtk_tree_view_find_node (selection->tree_view,
+                               end_path,
+                               &start_tree,
+                               &start_node);
+      _gtk_tree_view_find_node (selection->tree_view,
+                               start_path,
+                               &end_tree,
+                               &end_node);
+      break;
+    case 0:
+      _gtk_tree_view_find_node (selection->tree_view,
+                               start_path,
+                               &start_tree,
+                               &start_node);
+      end_tree = start_tree;
+      end_node = start_node;
+      break;
+    case 1:
+      _gtk_tree_view_find_node (selection->tree_view,
+                               start_path,
+                               &start_tree,
+                               &start_node);
+      _gtk_tree_view_find_node (selection->tree_view,
+                               end_path,
+                               &end_tree,
+                               &end_node);
+      break;
+    }
+
+  g_return_if_fail (start_node != NULL);
+  g_return_if_fail (end_node != NULL);
+
+  do
+    {
+      gtk_tree_selection_real_select_node (selection, start_tree, start_node, TRUE);
+
+      if (start_node == end_node)
+       return;
+
+      if (start_node->children)
+       {
+         start_tree = start_node->children;
+         start_node = start_tree->root;
+         while (start_node->left != start_tree->nil)
+           start_node = start_node->left;
+       }
+      else
+       {
+         gboolean done = FALSE;
+         do
+           {
+             start_node = _gtk_rbtree_next (start_tree, start_node);
+             if (start_node != NULL)
+               {
+                 done = TRUE;
+               }
+             else
+               {
+                 start_node = start_tree->parent_node;
+                 start_tree = start_tree->parent_tree;
+                 if (start_tree == NULL)
+                   /* we've run out of tree */
+                   /* This means we never found end node!! */
+                   return;
+               }
+           }
+         while (!done);
+       }
+    }
+  while (TRUE);
+}
+
+
+/* Called internally by gtktree_view.  It handles actually selecting
+ * the tree.  This should almost certainly ever be called by
+ * anywhere else */
+void
+_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
+                                         GtkRBNode        *node,
+                                         GtkRBTree        *tree,
+                                         GtkTreePath      *path,
+                                         GdkModifierType   state)
+{
+  gint flags;
+
+  if (((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) && (selection->tree_view->priv->anchor == NULL))
+    {
+      selection->tree_view->priv->anchor = gtk_tree_path_copy (path);
+      gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
+    }
+  else if ((state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_SHIFT_MASK|GDK_CONTROL_MASK))
+    {
+      gtk_tree_selection_select_range (selection,
+                                      selection->tree_view->priv->anchor,
+                                      path);
+    }
+  else if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+    {
+      flags = node->flags;
+      if (selection->type == GTK_TREE_SELECTION_SINGLE)
+       gtk_tree_selection_unselect_all (selection);
+      if (selection->tree_view->priv->anchor)
+       gtk_tree_path_free (selection->tree_view->priv->anchor);
+      selection->tree_view->priv->anchor = gtk_tree_path_copy (path);
+      if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED)
+       gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
+      else
+       gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
+    }
+  else if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+    {
+      gtk_tree_selection_unselect_all (selection);
+      gtk_tree_selection_select_range (selection,
+                                      selection->tree_view->priv->anchor,
+                                      path);
+    }
+  else
+    {
+      gtk_tree_selection_unselect_all (selection);
+      if (selection->tree_view->priv->anchor)
+       gtk_tree_path_free (selection->tree_view->priv->anchor);
+      selection->tree_view->priv->anchor = gtk_tree_path_copy (path);
+      gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
+    }
+}
+
diff --git a/gtk/gtktreeselection.h b/gtk/gtktreeselection.h
new file mode 100644 (file)
index 0000000..325cad8
--- /dev/null
@@ -0,0 +1,120 @@
+/* gtktreeselection.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_SELECTION_H__
+#define __GTK_TREE_SELECTION_H__
+
+#include <gobject/gobject.h>
+#include <gtk/gtkobject.h>
+#include <gtk/gtktreeview.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#warning "Die GTK_TREE_SELECTION, DIE"
+#undef GTK_TREE_SELECTION
+
+#define GTK_TYPE_TREE_SELECTION                        (gtk_tree_selection_get_type ())
+#define GTK_TREE_SELECTION(obj)                        (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_SELECTION, GtkTreeSelection))
+#define GTK_TREE_SELECTION_CLASS(klass)                (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_SELECTION, GtkTreeSelectionClass))
+#define GTK_IS_TREE_SELECTION(obj)             (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_SELECTION))
+#define GTK_IS_TREE_SELECTION_CLASS(klass)     (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_SELECTION))
+
+typedef enum
+{
+  GTK_TREE_SELECTION_SINGLE,
+  GTK_TREE_SELECTION_MULTI
+} GtkTreeSelectionType;
+
+typedef gboolean (* GtkTreeSelectionFunc)    (GtkTreeSelection  *selection,
+                                             GtkTreeModel      *model,
+                                             GtkTreePath       *path,
+                                             gpointer           data);
+typedef void (* GtkTreeSelectionForeachFunc) (GtkTreeModel      *model,
+                                             GtkTreeNode       *node,
+                                             gpointer           data);
+
+struct _GtkTreeSelection
+{
+  GtkObject parent;
+
+  GtkTreeView *tree_view;
+  GtkTreeSelectionType type;
+  GtkTreeSelectionFunc user_func;
+  gpointer user_data;
+};
+
+struct _GtkTreeSelectionClass
+{
+  GtkObjectClass parent_class;
+
+  void (* row_selected)   (GtkTreeView  *tree_view,
+                          GtkTreeModel *tree_model,
+                          GtkTreeNode  *node);
+  void (* row_unselected) (GtkTreeView  *tree_view,
+                          GtkTreeModel *tree_model,
+                          GtkTreeNode  *node);
+};
+
+
+GtkType          gtk_tree_selection_get_type            (void);
+GtkObject       *gtk_tree_selection_new                 (void);
+void             gtk_tree_selection_set_type            (GtkTreeSelection            *selection,
+                                                        GtkTreeSelectionType         type);
+void             gtk_tree_selection_set_select_function (GtkTreeSelection            *selection,
+                                                        GtkTreeSelectionFunc         func,
+                                                        gpointer                     data);
+gpointer         gtk_tree_selection_get_user_data       (GtkTreeSelection            *selection);
+
+
+/* Only meaningful if GTK_TREE_SELECTION_SINGLE is set */
+/* Use selected_foreach for GTK_TREE_SELECTION_MULTI */
+GtkTreeNode     *gtk_tree_selection_get_selected        (GtkTreeSelection            *selection);
+/* FIXME: Get a more convenient get_selection function????  one returning GSList?? */
+void             gtk_tree_selection_selected_foreach    (GtkTreeSelection            *selection,
+                                                        GtkTreeSelectionForeachFunc  func,
+                                                        gpointer                     data);
+void             gtk_tree_selection_select_path         (GtkTreeSelection            *selection,
+                                                        GtkTreePath                 *path);
+void             gtk_tree_selection_unselect_path       (GtkTreeSelection            *selection,
+                                                        GtkTreePath                 *path);
+void             gtk_tree_selection_select_node         (GtkTreeSelection            *selection,
+                                                        GtkTreeNode                 *tree_node);
+void             gtk_tree_selection_unselect_node       (GtkTreeSelection            *selection,
+                                                        GtkTreeNode                 *tree_node);
+void             gtk_tree_selection_select_all          (GtkTreeSelection            *selection);
+void             gtk_tree_selection_unselect_all        (GtkTreeSelection            *selection);
+void             gtk_tree_selection_select_range        (GtkTreeSelection            *selection,
+                                                        GtkTreePath                 *start_path,
+                                                        GtkTreePath                 *end_path);
+
+
+/*< private >*/
+GtkObject       *gtk_tree_selection_new_with_tree_view  (GtkTreeView                 *tree_view);
+void             gtk_tree_selection_set_tree_view       (GtkTreeSelection            *selection,
+                                                        GtkTreeView                 *tree_view);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_SELECTION_H__ */
+
diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c
new file mode 100644 (file)
index 0000000..b944e49
--- /dev/null
@@ -0,0 +1,655 @@
+/* gtktreestore.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreemodel.h"
+#include "gtktreestore.h"
+#include "gtktreedatalist.h"
+#include "gtksignal.h"
+#include <string.h>
+
+#define G_NODE(node) ((GNode *)node)
+
+static void           gtk_tree_store_init            (GtkTreeStore      *TreeStore);
+static void           gtk_tree_store_class_init      (GtkTreeStoreClass *klass);
+static gint           gtk_tree_store_get_n_columns   (GtkTreeModel      *tree_model);
+static GtkTreeNode    gtk_tree_store_get_node        (GtkTreeModel      *tree_model,
+                                                     GtkTreePath       *path);
+static GtkTreePath   *gtk_tree_store_get_path        (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static void           gtk_tree_store_node_get_value  (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node,
+                                                     gint               column,
+                                                     GValue            *value);
+static gboolean       gtk_tree_store_node_next       (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode       *node);
+static GtkTreeNode    gtk_tree_store_node_children   (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static gboolean       gtk_tree_store_node_has_child  (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static gint           gtk_tree_store_node_n_children (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+static GtkTreeNode    gtk_tree_store_node_nth_child  (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node,
+                                                     gint               n);
+static GtkTreeNode    gtk_tree_store_node_parent     (GtkTreeModel      *tree_model,
+                                                     GtkTreeNode        node);
+
+
+static GtkTreeModelClass *parent_class = NULL;
+
+
+GtkType
+gtk_tree_store_get_type (void)
+{
+  static GtkType tree_store_type = 0;
+
+  if (!tree_store_type)
+    {
+      static const GTypeInfo tree_store_info =
+      {
+        sizeof (GtkTreeStoreClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_tree_store_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkTreeStore),
+       0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_tree_store_init
+      };
+
+      tree_store_type = g_type_register_static (GTK_TYPE_TREE_MODEL, "GtkTreeStore", &tree_store_info);
+    }
+
+  return tree_store_type;
+}
+
+static void
+gtk_tree_store_class_init (GtkTreeStoreClass *klass)
+{
+  GtkObjectClass *object_class;
+  GtkTreeModelClass *tree_model_class;
+
+  object_class = (GtkObjectClass *) klass;
+  tree_model_class = (GtkTreeModelClass *) klass;
+
+  parent_class = gtk_type_class (gtk_tree_model_get_type ());
+
+  tree_model_class->get_n_columns = gtk_tree_store_get_n_columns;
+  tree_model_class->get_node = gtk_tree_store_get_node;
+  tree_model_class->get_path = gtk_tree_store_get_path;
+  tree_model_class->node_get_value = gtk_tree_store_node_get_value;
+  tree_model_class->node_next = gtk_tree_store_node_next;
+  tree_model_class->node_children = gtk_tree_store_node_children;
+  tree_model_class->node_has_child = gtk_tree_store_node_has_child;
+  tree_model_class->node_n_children = gtk_tree_store_node_n_children;
+  tree_model_class->node_nth_child = gtk_tree_store_node_nth_child;
+  tree_model_class->node_parent = gtk_tree_store_node_parent;
+}
+
+static void
+gtk_tree_store_init (GtkTreeStore *tree_store)
+{
+  tree_store->root = gtk_tree_store_node_new ();
+}
+
+GtkObject *
+gtk_tree_store_new (void)
+{
+  return GTK_OBJECT (gtk_type_new (gtk_tree_store_get_type ()));
+}
+
+GtkObject *
+gtk_tree_store_new_with_values (gint n_columns,
+                               ...)
+{
+  GtkObject *retval;
+  va_list args;
+  gint i;
+
+  g_return_val_if_fail (n_columns > 0, NULL);
+
+  retval = gtk_tree_store_new ();
+  gtk_tree_store_set_n_columns (GTK_TREE_STORE (retval),
+                               n_columns);
+
+  va_start (args, n_columns);
+  for (i = 0; i < n_columns; i++)
+    gtk_tree_store_set_column_type (GTK_TREE_STORE (retval),
+                                   i, va_arg (args, GType));
+
+  va_end (args);
+
+  return retval;
+}
+
+void
+gtk_tree_store_set_n_columns (GtkTreeStore *tree_store,
+                             gint          n_columns)
+{
+  GType *new_columns;
+
+  g_return_if_fail (tree_store != NULL);
+  g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+
+  if (tree_store->n_columns == n_columns)
+    return;
+
+  new_columns = g_new0 (GType, n_columns);
+  if (tree_store->column_headers)
+    {
+      /* copy the old header orders over */
+      if (n_columns >= tree_store->n_columns)
+       memcpy (new_columns, tree_store->column_headers, tree_store->n_columns * sizeof (gchar *));
+      else
+       memcpy (new_columns, tree_store->column_headers, n_columns * sizeof (GType));
+
+      g_free (tree_store->column_headers);
+    }
+
+  tree_store->column_headers = new_columns;
+  tree_store->n_columns = n_columns;
+}
+
+void
+gtk_tree_store_set_column_type (GtkTreeStore *tree_store,
+                               gint          column,
+                               GType         type)
+{
+  g_return_if_fail (tree_store != NULL);
+  g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+  g_return_if_fail (column >=0 && column < tree_store->n_columns);
+
+  tree_store->column_headers[column] = type;
+}
+
+/* fulfill the GtkTreeModel requirements */
+/* NOTE: GtkTreeStore::root is a GNode, that acts as the parent node.  However,
+ * it is not visible to the tree or to the user., and the path "1" refers to the
+ * first child of GtkTreeStore::root.
+ */
+static gint
+gtk_tree_store_get_n_columns (GtkTreeModel *tree_model)
+{
+  g_return_val_if_fail (tree_model != NULL, 0);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (tree_model), 0);
+
+  return GTK_TREE_STORE (tree_model)->n_columns;
+}
+
+static GtkTreeNode
+gtk_tree_store_get_node (GtkTreeModel *tree_model,
+                        GtkTreePath  *path)
+{
+  gint i;
+  GtkTreeNode *node;
+  gint *indices = gtk_tree_path_get_indices (path);
+
+  node = GTK_TREE_STORE (tree_model)->root;
+
+  for (i = 0; i < gtk_tree_path_get_depth (path); i ++)
+    {
+      node = (GtkTreeNode *) gtk_tree_store_node_nth_child (tree_model,
+                                                           (GtkTreeNode *) node,
+                                                           indices[i]);
+      if (node == NULL)
+       return NULL;
+    };
+  return (GtkTreeNode) node;
+}
+
+static GtkTreePath *
+gtk_tree_store_get_path (GtkTreeModel *tree_model,
+                        GtkTreeNode   node)
+{
+  GtkTreePath *retval;
+  GNode *tmp_node;
+  gint i = 0;
+
+  g_return_val_if_fail (tree_model != NULL, NULL);
+
+  if (node == NULL)
+    return NULL;
+  if (node == G_NODE (GTK_TREE_STORE (tree_model)->root))
+    return NULL;
+
+  if (G_NODE (node)->parent == G_NODE (GTK_TREE_STORE (tree_model)->root))
+    {
+      retval = gtk_tree_path_new ();
+      tmp_node = G_NODE (GTK_TREE_STORE (tree_model)->root)->children;
+    }
+  else
+    {
+      retval = gtk_tree_store_get_path (tree_model,
+                                       G_NODE (node)->parent);
+      tmp_node = G_NODE (node)->parent->children;
+    }
+
+  if (retval == NULL)
+    return NULL;
+  if (tmp_node == NULL)
+    {
+      gtk_tree_path_free (retval);
+      return NULL;
+    }
+
+  for (; tmp_node; tmp_node = tmp_node->next)
+    {
+      if (tmp_node == G_NODE (node))
+       break;
+      i++;
+    }
+  if (tmp_node == NULL)
+    {
+      /* We couldn't find node, meaning it's prolly not ours */
+      gtk_tree_path_free (retval);
+      return NULL;
+    }
+
+  gtk_tree_path_append_index (retval, i);
+
+  return retval;
+}
+
+
+static void
+gtk_tree_store_node_get_value (GtkTreeModel *tree_model,
+                              GtkTreeNode   node,
+                              gint          column,
+                              GValue       *value)
+{
+  GtkTreeDataList *list;
+  gint tmp_column = column;
+
+  g_return_if_fail (tree_model != NULL);
+  g_return_if_fail (GTK_IS_TREE_STORE (tree_model));
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (column < GTK_TREE_STORE (tree_model)->n_columns);
+
+  list = G_NODE (node)->data;
+
+  while (tmp_column-- > 0 && list)
+    list = list->next;
+
+  g_return_if_fail (list != NULL);
+
+  gtk_tree_data_list_node_to_value (list,
+                                   GTK_TREE_STORE (tree_model)->column_headers[column],
+                                   value);
+}
+
+static gboolean
+gtk_tree_store_node_next (GtkTreeModel  *tree_model,
+                         GtkTreeNode   *node)
+{
+  if (node == NULL || *node == NULL)
+    return FALSE;
+
+  *node = (GtkTreeNode *) G_NODE (*node)->next;
+  return (*node != NULL);
+}
+
+static GtkTreeNode
+gtk_tree_store_node_children (GtkTreeModel *tree_model,
+                             GtkTreeNode   node)
+{
+  return (GtkTreeNode) G_NODE (node)->children;
+}
+
+static gboolean
+gtk_tree_store_node_has_child (GtkTreeModel *tree_model,
+                              GtkTreeNode   node)
+{
+  return G_NODE (node)->children != NULL;
+}
+
+static gint
+gtk_tree_store_node_n_children (GtkTreeModel *tree_model,
+                               GtkTreeNode   node)
+{
+  gint i = 0;
+
+  node = (GtkTreeNode *) G_NODE (node)->children;
+  while (node != NULL)
+    {
+      i++;
+      node = (GtkTreeNode *) G_NODE (node)->next;
+    }
+
+  return i;
+}
+
+static GtkTreeNode
+gtk_tree_store_node_nth_child (GtkTreeModel *tree_model,
+                              GtkTreeNode   node,
+                              gint          n)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+
+  return (GtkTreeNode *) g_node_nth_child (G_NODE (node), n);
+}
+
+static GtkTreeNode
+gtk_tree_store_node_parent (GtkTreeModel *tree_model,
+                           GtkTreeNode   node)
+{
+  return (GtkTreeNode) G_NODE (node)->parent;
+}
+
+/* Public accessors */
+GtkTreeNode *
+gtk_tree_store_node_new (void)
+{
+  GtkTreeNode *retval;
+
+  retval = (GtkTreeNode *) g_node_new (NULL);
+  return retval;
+}
+
+/*
+ * This is a somewhat inelegant function that does a lot of list
+ * manipulations on it's own.
+ */
+void
+gtk_tree_store_node_set_cell (GtkTreeStore *tree_store,
+                             GtkTreeNode  *node,
+                             gint          column,
+                             GValue       *value)
+{
+  GtkTreeDataList *list;
+  GtkTreeDataList *prev;
+
+  g_return_if_fail (tree_store != NULL);
+  g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (column >= 0 && column < tree_store->n_columns);
+
+  prev = list = G_NODE (node)->data;
+
+  while (list != NULL)
+    {
+      if (column == 0)
+       {
+         gtk_tree_data_list_value_to_node (list, value);
+         return;
+       }
+
+      column--;
+      prev = list;
+      list = list->next;
+    }
+
+  if (G_NODE (node)->data == NULL)
+    {
+      G_NODE (node)->data = list = gtk_tree_data_list_alloc ();
+      list->next = NULL;
+    }
+  else
+    {
+      list = prev->next = gtk_tree_data_list_alloc ();
+      list->next = NULL;
+    }
+
+  while (column != 0)
+    {
+      list->next = gtk_tree_data_list_alloc ();
+      list = list->next;
+      list->next = NULL;
+      column --;
+    }
+  gtk_tree_data_list_value_to_node (list, value);
+}
+
+void
+gtk_tree_store_node_remove (GtkTreeStore *model,
+                           GtkTreeNode  *node)
+{
+  GtkTreePath *path;
+  GNode *parent;
+
+  g_return_if_fail (model != NULL);
+  g_return_if_fail (GTK_IS_TREE_STORE (model));
+  g_return_if_fail (node != NULL);
+  /* FIXME: if node is NULL, do I want to free the tree? */
+
+  parent = G_NODE (node)->parent;
+
+  path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+  g_node_destroy (G_NODE (node));
+  gtk_signal_emit_by_name (GTK_OBJECT (model),
+                          "node_deleted",
+                          path);
+  if (parent != G_NODE (model->root) && parent->children == NULL)
+    {
+      gtk_tree_path_up (path);
+      gtk_signal_emit_by_name (GTK_OBJECT (model),
+                              "node_child_toggled",
+                              path,
+                              parent);
+    }
+  gtk_tree_path_free (path);
+}
+
+GtkTreeNode *
+gtk_tree_store_node_insert (GtkTreeStore *model,
+                           GtkTreeNode  *parent,
+                           gint          position,
+                           GtkTreeNode  *node)
+{
+  GtkTreePath *path;
+
+  g_return_val_if_fail (model != NULL, node);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  if (parent == NULL)
+    parent = model->root;
+
+  g_node_insert (G_NODE (parent), position, G_NODE (node));
+
+  path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+  gtk_signal_emit_by_name (GTK_OBJECT (model),
+                          "node_inserted",
+                          path, node);
+  gtk_tree_path_free (path);
+
+  return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_insert_before (GtkTreeStore *model,
+                                  GtkTreeNode  *parent,
+                                  GtkTreeNode  *sibling,
+                                  GtkTreeNode  *node)
+{
+  GtkTreePath *path;
+
+  g_return_val_if_fail (model != NULL, node);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  if (parent == NULL && sibling == NULL)
+    parent = model->root;
+
+  if (parent == NULL)
+    parent = (GtkTreeNode *) G_NODE (sibling)->parent;
+
+  g_node_insert_before (G_NODE (parent), G_NODE (sibling), G_NODE (node));
+
+  path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+  gtk_signal_emit_by_name (GTK_OBJECT (model),
+                          "node_inserted",
+                          path, node);
+  gtk_tree_path_free (path);
+
+  return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_insert_after (GtkTreeStore *model,
+                                 GtkTreeNode  *parent,
+                                 GtkTreeNode  *sibling,
+                                 GtkTreeNode  *node)
+{
+  GtkTreePath *path;
+
+  g_return_val_if_fail (model != NULL, node);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  if (parent == NULL && sibling == NULL)
+    parent = model->root;
+
+  if (parent == NULL)
+    parent = (GtkTreeNode *) G_NODE (sibling)->parent;
+
+  g_node_insert_after (G_NODE (parent), G_NODE (sibling), G_NODE (node));
+
+  path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+  gtk_signal_emit_by_name (GTK_OBJECT (model),
+                          "node_inserted",
+                          path, node);
+  gtk_tree_path_free (path);
+  return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_prepend (GtkTreeStore *model,
+                            GtkTreeNode  *parent,
+                            GtkTreeNode  *node)
+{
+  g_return_val_if_fail (model != NULL, node);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  if (parent == NULL)
+    parent = model->root;
+
+  if (G_NODE (parent)->children == NULL)
+    {
+      GtkTreePath *path;
+      g_node_prepend (G_NODE (parent), G_NODE (node));
+      if (parent != model->root)
+       {
+         path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), parent);
+         gtk_signal_emit_by_name (GTK_OBJECT (model),
+                                  "node_child_toggled",
+                                  path,
+                                  parent);
+         gtk_tree_path_append_index (path, 1);
+       }
+      else
+       {
+         path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), G_NODE (parent)->children);
+       }
+      gtk_signal_emit_by_name (GTK_OBJECT (model),
+                              "node_inserted",
+                              path,
+                              G_NODE (parent)->children);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      gtk_tree_store_node_insert_after (model,
+                                        parent == model->root?NULL:parent,
+                                        NULL,
+                                        node);
+    }
+  return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_append (GtkTreeStore *model,
+                           GtkTreeNode  *parent,
+                           GtkTreeNode  *node)
+{
+  g_return_val_if_fail (model != NULL, node);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+  g_return_val_if_fail (node != NULL, node);
+
+  if (parent == NULL)
+    parent = model->root;
+
+  if (G_NODE (parent)->children == NULL)
+    {
+      GtkTreePath *path;
+      g_node_append (G_NODE (parent), G_NODE (node));
+      if (parent != model->root)
+       {
+         path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), parent);
+         gtk_signal_emit_by_name (GTK_OBJECT (model),
+                                  "node_child_toggled",
+                                  path,
+                                  parent);
+         gtk_tree_path_append_index (path, 1);
+       }
+      else
+       {
+         path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), G_NODE (parent)->children);
+       }
+      gtk_signal_emit_by_name (GTK_OBJECT (model),
+                              "node_inserted",
+                              path,
+                              G_NODE (parent)->children);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      gtk_tree_store_node_insert_before (model,
+                                        parent == model->root?NULL:parent,
+                                        NULL,
+                                        node);
+    }
+  return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_get_root (GtkTreeStore *model)
+{
+  g_return_val_if_fail (model != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), NULL);
+
+  return (GtkTreeNode *) model->root;
+}
+
+
+gboolean
+gtk_tree_store_node_is_ancestor (GtkTreeStore *model,
+                                GtkTreeNode  *node,
+                                GtkTreeNode  *descendant)
+{
+  g_return_val_if_fail (model != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), FALSE);
+  g_return_val_if_fail (node != NULL, FALSE);
+  g_return_val_if_fail (descendant != NULL, FALSE);
+
+  return g_node_is_ancestor (G_NODE (node), G_NODE (descendant));
+}
+
+
+gint
+gtk_tree_store_node_depth (GtkTreeStore *model,
+                          GtkTreeNode  *node)
+{
+  g_return_val_if_fail (model != NULL, 0);
+  g_return_val_if_fail (GTK_IS_TREE_STORE (model), 0);
+  g_return_val_if_fail (node != NULL, 0);
+
+  return g_node_depth (G_NODE (node));
+}
diff --git a/gtk/gtktreestore.h b/gtk/gtktreestore.h
new file mode 100644 (file)
index 0000000..dce0baf
--- /dev/null
@@ -0,0 +1,99 @@
+/* gtktreestore.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_STORE_H__
+#define __GTK_TREE_STORE_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_STORE                    (gtk_tree_store_get_type ())
+#define GTK_TREE_STORE(obj)                    (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_STORE, GtkTreeStore))
+#define GTK_TREE_STORE_CLASS(klass)            (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_STORE, GtkTreeStoreClass))
+#define GTK_IS_TREE_STORE(obj)                 (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_STORE))
+#define GTK_IS_TREE_STORE_CLASS(klass)         (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_STORE))
+
+typedef struct _GtkTreeStore       GtkTreeStore;
+typedef struct _GtkTreeStoreClass  GtkTreeStoreClass;
+
+struct _GtkTreeStore
+{
+  GtkTreeModel parent;
+  GtkTreeNode *root;
+  gint n_columns;
+  GType *column_headers;
+};
+
+struct _GtkTreeStoreClass
+{
+  GtkTreeModelClass parent_class;
+};
+
+GtkType      gtk_tree_store_get_type           (void);
+GtkObject   *gtk_tree_store_new                (void);
+GtkObject   *gtk_tree_store_new_with_values    (gint            n_columns,
+                                               ...);
+void         gtk_tree_store_set_n_columns      (GtkTreeStore   *tree_store,
+                                               gint            n_columns);
+void         gtk_tree_store_set_column_type    (GtkTreeStore   *store,
+                                               gint            column,
+                                               GType           type);
+
+GtkTreeNode *gtk_tree_store_node_new           (void);
+void         gtk_tree_store_node_set_cell      (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *node,
+                                               gint            column,
+                                               GValue *value);
+void         gtk_tree_store_node_remove        (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_tree_store_node_insert        (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *parent,
+                                               gint            position,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_tree_store_node_insert_before (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *parent,
+                                               GtkTreeNode    *sibling,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_tree_store_node_insert_after  (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *parent,
+                                               GtkTreeNode    *sibling,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_tree_store_node_prepend       (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *parent,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_tree_store_node_append        (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *parent,
+                                               GtkTreeNode    *node);
+GtkTreeNode *gtk_tree_store_node_get_root      (GtkTreeStore   *tree_store);
+gboolean     gtk_tree_store_node_is_ancestor   (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *node,
+                                               GtkTreeNode    *descendant);
+gint         gtk_tree_store_node_depth         (GtkTreeStore   *tree_store,
+                                               GtkTreeNode    *node);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_STORE_H__ */
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
new file mode 100644 (file)
index 0000000..f2bf4ad
--- /dev/null
@@ -0,0 +1,3384 @@
+/* gtktreeview.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "gtktreeview.h"
+#include "gtkrbtree.h"
+#include "gtktreeprivate.h"
+#include "gtkcellrenderer.h"
+#include "gtksignal.h"
+#include "gtkmain.h"
+#include "gtkbutton.h"
+#include "gtkalignment.h"
+#include "gtklabel.h"
+
+#include <gdk/gdkkeysyms.h>
+
+
+/* the width of the column resize windows */
+#define TREE_VIEW_DRAG_WIDTH 6
+#define TREE_VIEW_EXPANDER_WIDTH 14
+#define TREE_VIEW_EXPANDER_HEIGHT 14
+#define TREE_VIEW_VERTICAL_SEPERATOR 2
+#define TREE_VIEW_HORIZONTAL_SEPERATOR 0
+
+
+typedef struct _GtkTreeViewChild GtkTreeViewChild;
+
+struct _GtkTreeViewChild
+{
+  GtkWidget *widget;
+  gint x;
+  gint y;
+};
+
+
+static void     gtk_tree_view_init                 (GtkTreeView      *tree_view);
+static void     gtk_tree_view_class_init           (GtkTreeViewClass *klass);
+
+/* widget signals */
+static void     gtk_tree_view_set_model_realized   (GtkTreeView      *tree_view);
+static void     gtk_tree_view_realize              (GtkWidget        *widget);
+static void     gtk_tree_view_unrealize            (GtkWidget        *widget);
+static void     gtk_tree_view_map                  (GtkWidget        *widget);
+static void     gtk_tree_view_size_request         (GtkWidget        *widget,
+                                                   GtkRequisition   *requisition);
+static void     gtk_tree_view_size_allocate        (GtkWidget        *widget,
+                                                   GtkAllocation    *allocation);
+static void     gtk_tree_view_draw                 (GtkWidget        *widget,
+                                                   GdkRectangle     *area);
+static gboolean gtk_tree_view_expose               (GtkWidget        *widget,
+                                                   GdkEventExpose   *event);
+static gboolean gtk_tree_view_motion               (GtkWidget        *widget,
+                                                   GdkEventMotion   *event);
+static gboolean gtk_tree_view_enter_notify         (GtkWidget        *widget,
+                                                   GdkEventCrossing *event);
+static gboolean gtk_tree_view_leave_notify         (GtkWidget        *widget,
+                                                   GdkEventCrossing *event);
+static gboolean gtk_tree_view_button_press         (GtkWidget        *widget,
+                                                   GdkEventButton   *event);
+static gboolean gtk_tree_view_button_release       (GtkWidget        *widget,
+                                                   GdkEventButton   *event);
+static void     gtk_tree_view_draw_focus           (GtkWidget        *widget);
+static gint     gtk_tree_view_focus_in             (GtkWidget        *widget,
+                                                   GdkEventFocus    *event);
+static gint     gtk_tree_view_focus_out            (GtkWidget        *widget,
+                                                   GdkEventFocus    *event);
+static gint     gtk_tree_view_focus                (GtkContainer     *container,
+                                                   GtkDirectionType  direction);
+
+/* container signals */
+static void     gtk_tree_view_remove               (GtkContainer     *container,
+                                                   GtkWidget        *widget);
+static void     gtk_tree_view_forall               (GtkContainer     *container,
+                                                   gboolean          include_internals,
+                                                   GtkCallback       callback,
+                                                   gpointer          callback_data);
+
+/* tree_model signals */
+static void     gtk_tree_view_set_adjustments      (GtkTreeView      *tree_view,
+                                                   GtkAdjustment    *hadj,
+                                                   GtkAdjustment    *vadj);
+static void     gtk_tree_view_node_changed         (GtkTreeModel     *model,
+                                                   GtkTreePath      *path,
+                                                   GtkTreeNode      *tree_node,
+                                                   gpointer          data);
+static void     gtk_tree_view_node_inserted        (GtkTreeModel     *model,
+                                                   GtkTreePath      *path,
+                                                   GtkTreeNode      *tree_node,
+                                                   gpointer          data);
+static void     gtk_tree_view_node_child_toggled   (GtkTreeModel     *model,
+                                                   GtkTreePath      *path,
+                                                   GtkTreeNode      *tree_node,
+                                                   gpointer          data);
+static void     gtk_tree_view_node_deleted         (GtkTreeModel     *model,
+                                                   GtkTreePath      *path,
+                                                   gpointer          data);
+
+/* Internal functions */
+static void     gtk_tree_view_draw_arrow           (GtkTreeView      *tree_view,
+                                                   GtkRBNode        *node,
+                                                   gint              offset,
+                                                   gint              x,
+                                                   gint              y);
+static gint     gtk_tree_view_new_column_width     (GtkTreeView      *tree_view,
+                                                   gint              i,
+                                                   gint             *x);
+static void     gtk_tree_view_adjustment_changed   (GtkAdjustment    *adjustment,
+                                                   GtkTreeView      *tree_view);
+static gint     gtk_tree_view_insert_node_height   (GtkTreeView      *tree_view,
+                                                   GtkRBTree        *tree,
+                                                   GtkTreeNode       node,
+                                                   gint              depth);
+static void     gtk_tree_view_build_tree           (GtkTreeView      *tree_view,
+                                                   GtkRBTree        *tree,
+                                                   GtkTreeNode       node,
+                                                   gint              depth,
+                                                   gboolean          recurse,
+                                                   gboolean          calc_bounds);
+static void     gtk_tree_view_calc_size            (GtkTreeView      *priv,
+                                                   GtkRBTree        *tree,
+                                                   GtkTreeNode       node,
+                                                   gint              depth);
+static gboolean gtk_tree_view_discover_dirty_node  (GtkTreeView      *tree_view,
+                                                   GtkTreeNode       node,
+                                                   gint              depth,
+                                                   gint             *height);
+static void     gtk_tree_view_discover_dirty       (GtkTreeView      *tree_view,
+                                                   GtkRBTree        *tree,
+                                                   GtkTreeNode       node,
+                                                   gint              depth);
+static void     gtk_tree_view_check_dirty          (GtkTreeView      *tree_view);
+static void     gtk_tree_view_create_button        (GtkTreeView      *tree_view,
+                                                   gint              i);
+static void     gtk_tree_view_create_buttons       (GtkTreeView      *tree_view);
+static void     gtk_tree_view_button_clicked       (GtkWidget        *widget,
+                                                   gpointer          data);
+static void     gtk_tree_view_clamp_node_visible   (GtkTreeView      *tree_view,
+                                                   GtkRBTree        *tree,
+                                                   GtkRBNode        *node);
+
+
+
+static GtkContainerClass *parent_class = NULL;
+
+
+/* Class Functions */
+GtkType
+gtk_tree_view_get_type (void)
+{
+  static GtkType tree_view_type = 0;
+
+  if (!tree_view_type)
+    {
+      static const GTypeInfo tree_view_info =
+      {
+        sizeof (GtkTreeViewClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_tree_view_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (GtkTreeView),
+       0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_tree_view_init
+      };
+
+      tree_view_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTreeView", &tree_view_info);
+    }
+
+  return tree_view_type;
+}
+
+static void
+gtk_tree_view_class_init (GtkTreeViewClass *class)
+{
+  GtkObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+  GtkContainerClass *container_class;
+
+  object_class = (GtkObjectClass*) class;
+  widget_class = (GtkWidgetClass*) class;
+  container_class = (GtkContainerClass*) class;
+  parent_class = g_type_class_peek_parent (class);
+
+  widget_class->realize = gtk_tree_view_realize;
+  widget_class->unrealize = gtk_tree_view_unrealize;
+  widget_class->map = gtk_tree_view_map;
+  widget_class->size_request = gtk_tree_view_size_request;
+  widget_class->size_allocate = gtk_tree_view_size_allocate;
+  widget_class->draw = gtk_tree_view_draw;
+  widget_class->expose_event = gtk_tree_view_expose;
+  //  widget_class->key_press_event = gtk_tree_view_key_press;
+  widget_class->motion_notify_event = gtk_tree_view_motion;
+  widget_class->enter_notify_event = gtk_tree_view_enter_notify;
+  widget_class->leave_notify_event = gtk_tree_view_leave_notify;
+  widget_class->button_press_event = gtk_tree_view_button_press;
+  widget_class->button_release_event = gtk_tree_view_button_release;
+  widget_class->draw_focus = gtk_tree_view_draw_focus;
+  widget_class->focus_in_event = gtk_tree_view_focus_in;
+  widget_class->focus_out_event = gtk_tree_view_focus_out;
+
+  container_class->forall = gtk_tree_view_forall;
+  container_class->remove = gtk_tree_view_remove;
+  container_class->focus = gtk_tree_view_focus;
+
+  class->set_scroll_adjustments = gtk_tree_view_set_adjustments;
+
+  widget_class->set_scroll_adjustments_signal =
+    gtk_signal_new ("set_scroll_adjustments",
+                   GTK_RUN_LAST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkTreeViewClass, set_scroll_adjustments),
+                   gtk_marshal_NONE__POINTER_POINTER,
+                   GTK_TYPE_NONE, 2,
+                   GTK_TYPE_POINTER, GTK_TYPE_POINTER);
+}
+
+static void
+gtk_tree_view_init (GtkTreeView *tree_view)
+{
+  tree_view->priv = g_new0 (GtkTreeViewPrivate, 1);
+
+  GTK_WIDGET_UNSET_FLAGS (tree_view, GTK_NO_WINDOW);
+  GTK_WIDGET_SET_FLAGS (tree_view, GTK_CAN_FOCUS);
+
+  tree_view->priv->flags = GTK_TREE_VIEW_IS_LIST | GTK_TREE_VIEW_SHOW_EXPANDERS | GTK_TREE_VIEW_DRAW_KEYFOCUS | GTK_TREE_VIEW_HEADERS_VISIBLE;
+  tree_view->priv->tab_offset = TREE_VIEW_EXPANDER_WIDTH;
+  tree_view->priv->columns = 0;
+  tree_view->priv->column = NULL;
+  tree_view->priv->button_pressed_node = NULL;
+  tree_view->priv->button_pressed_tree = NULL;
+  tree_view->priv->prelight_node = NULL;
+  tree_view->priv->prelight_offset = 0;
+  tree_view->priv->header_height = 1;
+  tree_view->priv->x_drag = 0;
+  tree_view->priv->drag_pos = -1;
+  tree_view->priv->selection = NULL;
+  tree_view->priv->anchor = NULL;
+  tree_view->priv->cursor = NULL;
+  gtk_tree_view_set_adjustments (tree_view, NULL, NULL);
+  _gtk_tree_view_set_size (tree_view, 0, 0);
+}
+
+/* Widget methods
+ */
+
+static void
+gtk_tree_view_realize_buttons (GtkTreeView *tree_view)
+{
+  GList *list;
+  GtkTreeViewColumn *column;
+  GdkWindowAttr attr;
+  guint attributes_mask;
+
+  if (!GTK_WIDGET_REALIZED (tree_view) || tree_view->priv->header_window == NULL)
+    return;
+
+  attr.window_type = GDK_WINDOW_CHILD;
+  attr.wclass = GDK_INPUT_ONLY;
+  attr.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
+  attr.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
+  attr.event_mask = gtk_widget_get_events (GTK_WIDGET (tree_view));
+  attr.event_mask = (GDK_BUTTON_PRESS_MASK |
+                    GDK_BUTTON_RELEASE_MASK |
+                    GDK_POINTER_MOTION_MASK |
+                    GDK_POINTER_MOTION_HINT_MASK |
+                    GDK_KEY_PRESS_MASK);
+  attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
+  attr.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+  tree_view->priv->cursor_drag = attr.cursor;
+
+  attr.y = 0;
+  attr.width = TREE_VIEW_DRAG_WIDTH;
+  attr.height = tree_view->priv->header_height;
+
+  for (list = tree_view->priv->column; list; list = list->next)
+    {
+      column = list->data;
+      if (column->button)
+       {
+         if (column->visible == FALSE)
+           continue;
+         gtk_widget_set_parent_window (column->button,
+                                       tree_view->priv->header_window);
+         gtk_widget_show (column->button);
+
+         attr.x = (column->button->allocation.x + column->button->allocation.width) - 3;
+
+         column->window = gdk_window_new (tree_view->priv->header_window,
+                                          &attr, attributes_mask);
+         gdk_window_set_user_data (column->window, tree_view);
+       }
+    }
+}
+
+static void
+gtk_tree_view_realize (GtkWidget *widget)
+{
+  GList *tmp_list;
+  GtkTreeView *tree_view;
+  GdkGCValues values;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP) &&
+      tree_view->priv->model)
+    gtk_tree_view_set_model_realized (tree_view);
+
+  gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget));
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  /* Make the main, clipping window */
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                  &attributes, attributes_mask);
+  gdk_window_set_user_data (widget->window, widget);
+
+  /* Make the window for the tree */
+  attributes.x = 0;
+  attributes.y = 0;
+  attributes.width = tree_view->priv->width;
+  attributes.height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
+  attributes.event_mask = GDK_EXPOSURE_MASK |
+    GDK_SCROLL_MASK |
+    GDK_POINTER_MOTION_MASK |
+    GDK_ENTER_NOTIFY_MASK |
+    GDK_LEAVE_NOTIFY_MASK |
+    GDK_BUTTON_PRESS_MASK |
+    GDK_BUTTON_RELEASE_MASK |
+    gtk_widget_get_events (widget);
+
+  tree_view->priv->bin_window = gdk_window_new (widget->window,
+                                               &attributes, attributes_mask);
+  gdk_window_set_user_data (tree_view->priv->bin_window, widget);
+
+  /* Make the column header window */
+  attributes.x = 0;
+  attributes.y = 0;
+  attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
+  attributes.height = tree_view->priv->header_height;
+  attributes.event_mask = (GDK_EXPOSURE_MASK |
+                          GDK_SCROLL_MASK |
+                          GDK_BUTTON_PRESS_MASK |
+                          GDK_BUTTON_RELEASE_MASK |
+                          GDK_KEY_PRESS_MASK |
+                          GDK_KEY_RELEASE_MASK) |
+    gtk_widget_get_events (widget);
+
+  tree_view->priv->header_window = gdk_window_new (widget->window,
+                                                  &attributes, attributes_mask);
+  gdk_window_set_user_data (tree_view->priv->header_window, widget);
+
+
+  values.foreground = (widget->style->white.pixel==0 ?
+                      widget->style->black:widget->style->white);
+  values.function = GDK_XOR;
+  values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+  tree_view->priv->xor_gc = gdk_gc_new_with_values (widget->window,
+                                                   &values,
+                                                   GDK_GC_FOREGROUND |
+                                                   GDK_GC_FUNCTION |
+                                                   GDK_GC_SUBWINDOW);
+  /* Add them all up. */
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  gdk_window_set_background (widget->window, &widget->style->base[widget->state]);
+  gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
+  gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
+
+  tmp_list = tree_view->priv->children;
+  while (tmp_list)
+    {
+      GtkTreeViewChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
+    }
+  gtk_tree_view_realize_buttons (GTK_TREE_VIEW (widget));
+  _gtk_tree_view_set_size (GTK_TREE_VIEW (widget), -1, -1);
+}
+
+static void
+gtk_tree_view_unrealize (GtkWidget *widget)
+{
+  GtkTreeView *tree_view;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  gdk_window_set_user_data (tree_view->priv->bin_window, NULL);
+  gdk_window_destroy (tree_view->priv->bin_window);
+  tree_view->priv->bin_window = NULL;
+
+  gdk_window_set_user_data (tree_view->priv->header_window, NULL);
+  gdk_window_destroy (tree_view->priv->header_window);
+  tree_view->priv->header_window = NULL;
+
+  gdk_gc_destroy (tree_view->priv->xor_gc);
+  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_tree_view_map (GtkWidget *widget)
+{
+  GList *tmp_list;
+  GtkTreeView *tree_view;
+  GList *list;
+  GtkTreeViewColumn *column;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+
+  tmp_list = tree_view->priv->children;
+  while (tmp_list)
+    {
+      GtkTreeViewChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget))
+       {
+         if (!GTK_WIDGET_MAPPED (child->widget))
+           gtk_widget_map (child->widget);
+       }
+    }
+  gdk_window_show (tree_view->priv->bin_window);
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+    {
+      for (list = tree_view->priv->column; list; list = list->next)
+       {
+         column = list->data;
+         gtk_widget_map (column->button);
+       }
+      for (list = tree_view->priv->column; list; list = list->next)
+       {
+         column = list->data;
+         if (column->visible == FALSE)
+           continue;
+         if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
+           {
+             gdk_window_raise (column->window);
+             gdk_window_show (column->window);
+           }
+         else
+           gdk_window_hide (column->window);
+       }
+      gdk_window_show (tree_view->priv->header_window);
+    }
+  gdk_window_show (widget->window);
+}
+
+static void
+gtk_tree_view_size_request (GtkWidget      *widget,
+                           GtkRequisition *requisition)
+{
+  GtkTreeView *tree_view;
+  GList *tmp_list;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  requisition->width = 200;
+  requisition->height = 200;
+
+  tmp_list = tree_view->priv->children;
+
+  while (tmp_list)
+    {
+      GtkTreeViewChild *child = tmp_list->data;
+      GtkRequisition child_requisition;
+
+      tmp_list = tmp_list->next;
+
+      gtk_widget_size_request (child->widget, &child_requisition);
+    }
+}
+
+static void
+gtk_tree_view_size_allocate_buttons (GtkWidget *widget)
+{
+  GtkTreeView *tree_view;
+  GList *list;
+  GList *last_column;
+  GtkTreeViewColumn *column;
+  GtkAllocation allocation;
+  gint width = 0;
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  allocation.y = 0;
+  allocation.height = tree_view->priv->header_height;
+
+  for (last_column = g_list_last (tree_view->priv->column);
+       last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible);
+       last_column = last_column->prev)
+    ;
+
+  if (last_column == NULL)
+    return;
+
+  for (list = tree_view->priv->column; list != last_column; list = list->next)
+    {
+      column = list->data;
+
+      if (!column->visible)
+       continue;
+
+      allocation.x = width;
+      allocation.width = column->size;
+      width += column->size;
+      gtk_widget_size_allocate (column->button, &allocation);
+
+      if (column->window)
+       gdk_window_move (column->window, width - TREE_VIEW_DRAG_WIDTH/2, 0);
+    }
+  column = list->data;
+  allocation.x = width;
+  allocation.width = MAX (widget->allocation.width, tree_view->priv->width) - width;
+  gtk_widget_size_allocate (column->button, &allocation);
+  if (column->window)
+    gdk_window_move (column->window,
+                    allocation.x +allocation.width - TREE_VIEW_DRAG_WIDTH/2,
+                    0);
+}
+
+static void
+gtk_tree_view_size_allocate (GtkWidget     *widget,
+                            GtkAllocation *allocation)
+{
+  GList *tmp_list;
+  GtkTreeView *tree_view;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  widget->allocation = *allocation;
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  tmp_list = tree_view->priv->children;
+
+  while (tmp_list)
+    {
+      GtkAllocation allocation;
+      GtkRequisition requisition;
+
+      GtkTreeViewChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      allocation.x = child->x;
+      allocation.y = child->y;
+      gtk_widget_get_child_requisition (child->widget, &requisition);
+      allocation.width = requisition.width;
+      allocation.height = requisition.height;
+
+      gtk_widget_size_allocate (child->widget, &allocation);
+    }
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      gdk_window_move_resize (widget->window,
+                             allocation->x, allocation->y,
+                             allocation->width, allocation->height);
+      gdk_window_move_resize (tree_view->priv->header_window,
+                             0, 0,
+                             MAX (tree_view->priv->width, allocation->width),
+                             tree_view->priv->header_height);
+    }
+
+  tree_view->priv->hadjustment->page_size = allocation->width;
+  tree_view->priv->hadjustment->page_increment = allocation->width / 2;
+  tree_view->priv->hadjustment->lower = 0;
+  tree_view->priv->hadjustment->upper = tree_view->priv->width;
+  if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
+    tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
+  gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed");
+
+  tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
+  tree_view->priv->vadjustment->page_increment = (allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
+  tree_view->priv->vadjustment->lower = 0;
+  tree_view->priv->vadjustment->upper = tree_view->priv->height;
+  if (tree_view->priv->vadjustment->value + allocation->height > tree_view->priv->height)
+    gtk_adjustment_set_value (tree_view->priv->vadjustment,
+                             (gfloat) MAX (tree_view->priv->height - allocation->height, 0));
+  gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
+
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP))
+    gtk_tree_view_size_allocate_buttons (widget);
+}
+
+static void
+gtk_tree_view_draw (GtkWidget    *widget,
+                   GdkRectangle *area)
+{
+  GList *tmp_list;
+  GtkTreeView *tree_view;
+  GtkTreeViewColumn *column;
+  GdkRectangle child_area;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  /* We don't have any way of telling themes about this properly,
+   * so we just assume a background pixmap
+   */
+  if (!GTK_WIDGET_APP_PAINTABLE (widget))
+    {
+      gdk_window_clear_area (tree_view->priv->bin_window,
+                            area->x, area->y, area->width, area->height);
+      gdk_window_clear_area (tree_view->priv->header_window,
+                            area->x, area->y, area->width, area->height);
+    }
+
+  tmp_list = tree_view->priv->children;
+  while (tmp_list)
+    {
+      GtkTreeViewChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      if (gtk_widget_intersect (child->widget, area, &child_area))
+       gtk_widget_draw (child->widget, &child_area);
+    }
+  for (tmp_list = tree_view->priv->column; tmp_list; tmp_list = tmp_list->next)
+    {
+      column = tmp_list->data;
+      if (!column->visible)
+       continue;
+      if (column->button &&
+         gtk_widget_intersect(column->button, area, &child_area))
+       gtk_widget_draw (column->button, &child_area);
+    }
+}
+
+/* Warning: Very scary function.
+ * Modify at your own risk
+ */
+static gboolean
+gtk_tree_view_bin_expose (GtkWidget      *widget,
+                         GdkEventExpose *event)
+{
+  GtkTreeView *tree_view;
+  GtkTreePath *path;
+  GtkRBTree *tree;
+  GList *list;
+  GtkRBNode *node, *last_node = NULL;
+  GtkRBNode *cursor = NULL;
+  GtkRBTree *cursor_tree = NULL, *last_tree = NULL;
+  GtkTreeNode tree_node;
+  GtkCellRenderer *cell;
+  gint new_y;
+  gint y_offset, x_offset, cell_offset;
+  gint i, max_height;
+  gint depth;
+  GdkRectangle background_area;
+  GdkRectangle cell_area;
+  guint flags;
+  gboolean last_selected;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (tree_view->priv->tree == NULL)
+    return TRUE;
+
+  gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget));
+  /* we want to account for a potential HEADER offset.
+   * That is, if the header exists, we want to offset our event by its
+   * height to find the right node.
+   */
+  new_y = (event->area.y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):event->area.y;
+  y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
+                                      new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+                                      &tree,
+                                      &node) + new_y - event->area.y;
+  if (node == NULL)
+    return TRUE;
+
+  /* See if the last node was selected */
+  _gtk_rbtree_prev_full (tree, node, &last_tree, &last_node);
+  last_selected = (last_node && GTK_RBNODE_FLAG_SET (last_node, GTK_RBNODE_IS_SELECTED));
+
+  /* find the path for the node */
+  path = _gtk_tree_view_find_path ((GtkTreeView *)widget,
+                                  tree,
+                                  node);
+  tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+  depth = gtk_tree_path_get_depth (path);
+  gtk_tree_path_free (path);
+
+  if (tree_view->priv->cursor)
+    _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor);
+
+  /* Actually process the expose event.  To do this, we want to
+   * start at the first node of the event, and walk the tree in
+   * order, drawing each successive node.
+   */
+
+  do
+    {
+      /* Need to think about this more.
+        if (tree_view->priv->show_expanders)
+        max_height = MAX (TREE_VIEW_EXPANDER_MIN_HEIGHT, GTK_RBNODE_GET_HEIGHT (node));
+        else
+      */
+      max_height = GTK_RBNODE_GET_HEIGHT (node);
+
+      x_offset = -event->area.x;
+      cell_offset = 0;
+
+      background_area.y = y_offset + event->area.y + TREE_VIEW_VERTICAL_SEPERATOR;
+      background_area.height = max_height - TREE_VIEW_VERTICAL_SEPERATOR;
+      flags = 0;
+
+      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT))
+       flags |= GTK_CELL_RENDERER_PRELIT;
+
+      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+       {
+         flags |= GTK_CELL_RENDERER_SELECTED;
+
+         /* Draw the selection */
+         gdk_draw_rectangle (event->window,
+                             GTK_WIDGET (tree_view)->style->bg_gc [GTK_STATE_SELECTED],
+                             TRUE,
+                             event->area.x,
+                             background_area.y - (last_selected?TREE_VIEW_VERTICAL_SEPERATOR:0),
+                             event->area.width,
+                             background_area.height + (last_selected?TREE_VIEW_VERTICAL_SEPERATOR:0));
+         last_selected = TRUE;
+       }
+      else
+       {
+         last_selected = FALSE;
+       }
+
+      for (i = 0, list = tree_view->priv->column; i < tree_view->priv->columns; i++, list = list->next)
+       {
+         GtkTreeViewColumn *column = list->data;
+
+         if (!column->visible)
+           continue;
+
+         cell = column->cell;
+         gtk_tree_view_column_set_cell_data (column,
+                                             tree_view->priv->model,
+                                             tree_node);
+
+         background_area.x = cell_offset;
+         background_area.width = TREE_VIEW_COLUMN_SIZE (column);
+         if (i == 0 && TREE_VIEW_DRAW_EXPANDERS(tree_view))
+           {
+             cell_area = background_area;
+             cell_area.x += depth*tree_view->priv->tab_offset;
+             cell_area.width -= depth*tree_view->priv->tab_offset;
+             gtk_cell_renderer_render (cell,
+                                       event->window,
+                                       widget,
+                                       &background_area,
+                                       &cell_area,
+                                       &event->area,
+                                       flags);
+             if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
+               {
+                 gint x, y;
+                 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, 0);
+                 gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+                                           node,
+                                           event->area.y + y_offset,
+                                           x, y);
+               }
+           }
+         else
+           {
+             cell_area = background_area;
+             gtk_cell_renderer_render (cell,
+                                       event->window,
+                                       widget,
+                                       &background_area,
+                                       &cell_area,
+                                       &event->area,
+                                       flags);
+           }
+         cell_offset += TREE_VIEW_COLUMN_SIZE (column);
+       }
+
+      if (node == cursor &&
+         GTK_WIDGET_HAS_FOCUS (widget))
+       gtk_tree_view_draw_focus (widget);
+
+      y_offset += max_height;
+      if (node->children)
+       {
+         tree = node->children;
+         node = tree->root;
+         while (node->left != tree->nil)
+           node = node->left;
+         tree_node = gtk_tree_model_node_children (tree_view->priv->model, tree_node);
+         cell = gtk_tree_view_get_column (tree_view, 0)->cell;
+         depth++;
+
+         /* Sanity Check! */
+         TREE_VIEW_INTERNAL_ASSERT (tree_node != NULL, FALSE);
+       }
+      else
+       {
+         gboolean done = FALSE;
+         do
+           {
+             node = _gtk_rbtree_next (tree, node);
+             if (node != NULL)
+               {
+                 gtk_tree_model_node_next (tree_view->priv->model, &tree_node);
+                 cell = gtk_tree_view_get_column (tree_view, 0)->cell;
+                 done = TRUE;
+
+                 /* Sanity Check! */
+                 TREE_VIEW_INTERNAL_ASSERT (tree_node != NULL, FALSE);
+               }
+             else
+               {
+                 node = tree->parent_node;
+                 tree = tree->parent_tree;
+                 if (tree == NULL)
+                   /* we've run out of tree.  It's okay though, as we'd only break
+                    * out of the while loop below. */
+                   return TRUE;
+                 tree_node = gtk_tree_model_node_parent (tree_view->priv->model, tree_node);
+                 depth--;
+
+                 /* Sanity check */
+                 TREE_VIEW_INTERNAL_ASSERT (tree_node != NULL, FALSE);
+               }
+           }
+         while (!done);
+       }
+    }
+  while (y_offset < event->area.height);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_tree_view_expose (GtkWidget      *widget,
+                     GdkEventExpose *event)
+{
+  GtkTreeView *tree_view;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (event->window == tree_view->priv->bin_window)
+    return gtk_tree_view_bin_expose (widget, event);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_tree_view_motion (GtkWidget *widget,
+                     GdkEventMotion  *event)
+{
+  GtkTreeView *tree_view;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+  gint new_y;
+  gint y_offset;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+    {
+      gint x;
+      gint new_width;
+
+      if (event->is_hint || event->window != widget->window)
+       gtk_widget_get_pointer (widget, &x, NULL);
+      else
+       x = event->x;
+
+      new_width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), tree_view->priv->drag_pos, &x);
+      if (x != tree_view->priv->x_drag)
+       {
+         gtk_tree_view_column_set_size (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), tree_view->priv->drag_pos), new_width);
+       }
+
+      /* FIXME: We need to scroll */
+      _gtk_tree_view_set_size (GTK_TREE_VIEW (widget), -1, tree_view->priv->height);
+      return FALSE;
+    }
+
+  /* Sanity check it */
+  if (event->window != tree_view->priv->bin_window)
+    return FALSE;
+  if (tree_view->priv->tree == NULL)
+    return FALSE;
+
+  if (tree_view->priv->prelight_node != NULL)
+    {
+      if ((((gint) event->y - TREE_VIEW_HEADER_HEIGHT (tree_view) < tree_view->priv->prelight_offset) ||
+          ((gint) event->y - TREE_VIEW_HEADER_HEIGHT (tree_view) >=
+           (tree_view->priv->prelight_offset + GTK_RBNODE_GET_HEIGHT (tree_view->priv->prelight_node))) ||
+          ((gint) event->x > tree_view->priv->tab_offset)))
+       /* We need to unprelight the old one. */
+       {
+         if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+           {
+             GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
+             gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+                                       tree_view->priv->prelight_node,
+                                       tree_view->priv->prelight_offset,
+                                       event->x,
+                                       event->y);
+             GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+           }
+
+         GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
+         tree_view->priv->prelight_node = NULL;
+         tree_view->priv->prelight_tree = NULL;
+         tree_view->priv->prelight_offset = 0;
+       }
+    }
+
+  new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
+  y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+                                      &tree,
+                                      &node) + new_y - (gint)event->y;
+
+  if (node == NULL)
+    return TRUE;
+
+  /* If we are currently pressing down a button, we don't want to prelight anything else. */
+  if ((tree_view->priv->button_pressed_node != NULL) &&
+      (tree_view->priv->button_pressed_node != node))
+    return TRUE;
+
+  /* Do we want to prelight a tab? */
+  GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+  if (event->x <= tree_view->priv->tab_offset &&
+      event->x >= 0 &&
+      ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT))
+    {
+      tree_view->priv->prelight_offset = event->y+y_offset;
+      GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+    }
+
+  tree_view->priv->prelight_node = node;
+  tree_view->priv->prelight_tree = tree;
+  tree_view->priv->prelight_offset = event->y+y_offset;
+
+  GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
+  gtk_widget_queue_draw (widget);
+
+  return TRUE;
+}
+
+/* Is this function necessary? Can I get an enter_notify event w/o either
+ * an expose event or a mouse motion event?
+ */
+static gboolean
+gtk_tree_view_enter_notify (GtkWidget        *widget,
+                           GdkEventCrossing *event)
+{
+  GtkTreeView *tree_view;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+  gint new_y;
+  gint y_offset;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  /* Sanity check it */
+  if (event->window != tree_view->priv->bin_window)
+    return FALSE;
+  if (tree_view->priv->tree == NULL)
+    return FALSE;
+
+  if ((tree_view->priv->button_pressed_node != NULL) &&
+      (tree_view->priv->button_pressed_node != node))
+    return TRUE;
+
+  /* find the node internally */
+  new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
+  y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
+                                      new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+                                      &tree,
+                                      &node) + new_y - (gint)event->y;
+
+  if (node == NULL)
+    return FALSE;
+
+  /* Do we want to prelight a tab? */
+  GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+  if (event->x <= tree_view->priv->tab_offset &&
+      event->x >= 0 &&
+      ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT))
+    {
+      tree_view->priv->prelight_offset = event->y+y_offset;
+      GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+    }
+
+  tree_view->priv->prelight_node = node;
+  tree_view->priv->prelight_tree = tree;
+  tree_view->priv->prelight_offset = event->y+y_offset;
+
+  GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
+  gtk_widget_queue_draw (widget);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_tree_view_leave_notify (GtkWidget        *widget,
+                           GdkEventCrossing *event)
+{
+  GtkTreeView *tree_view;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (tree_view->priv->prelight_node != NULL)
+    {
+      GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
+      tree_view->priv->prelight_node = NULL;
+      tree_view->priv->prelight_tree = NULL;
+      tree_view->priv->prelight_offset = 0;
+      GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+      gtk_widget_queue_draw (widget);
+    }
+  return TRUE;
+}
+
+static gboolean
+gtk_tree_view_button_press (GtkWidget      *widget,
+                           GdkEventButton *event)
+{
+  GtkTreeView *tree_view;
+  GList *list;
+  GtkTreeViewColumn *column;
+  gint i;
+  GdkRectangle background_area;
+  GdkRectangle cell_area;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (event->window == tree_view->priv->bin_window)
+    {
+      GtkRBNode *node;
+      GtkRBTree *tree;
+      GtkTreePath *path;
+      gchar *path_string;
+      gint depth;
+      gint new_y;
+      gint y_offset;
+
+      if (!GTK_WIDGET_HAS_FOCUS (widget))
+       gtk_widget_grab_focus (widget);
+      GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+      /* are we in an arrow? */
+      if (tree_view->priv->prelight_node != FALSE && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+       {
+         if (event->button == 1)
+           {
+             gtk_grab_add (widget);
+             tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
+             tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
+             gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+                                       tree_view->priv->prelight_node,
+                                       tree_view->priv->prelight_offset,
+                                       event->x,
+                                       event->y);
+           }
+         return TRUE;
+       }
+
+      /* find the node that was clicked */
+      new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
+      y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
+                                          new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+                                          &tree,
+                                          &node) + new_y - (gint)event->y;
+
+      if (node == NULL)
+       /* We clicked in dead space */
+       return TRUE;
+
+      /* Get the path and the node */
+      path = _gtk_tree_view_find_path (tree_view, tree, node);
+      depth = gtk_tree_path_get_depth (path);
+      background_area.y = y_offset + event->y + TREE_VIEW_VERTICAL_SEPERATOR;
+      background_area.height = GTK_RBNODE_GET_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPERATOR;
+      background_area.x = 0;
+      /* Let the cell have a chance at selecting it. */
+
+      for (i = 0, list = tree_view->priv->column; i < tree_view->priv->columns; i++, list = list->next)
+       {
+         GtkTreeViewColumn *column = list->data;
+         GtkCellRenderer *cell;
+         GtkTreeNode *tree_node;
+
+         if (!column->visible)
+           continue;
+
+         background_area.width = TREE_VIEW_COLUMN_SIZE (column);
+         if (i == 0 && TREE_VIEW_DRAW_EXPANDERS(tree_view))
+           {
+             cell_area = background_area;
+             cell_area.x += depth*tree_view->priv->tab_offset;
+             cell_area.width -= depth*tree_view->priv->tab_offset;
+           }
+         else
+           {
+             cell_area = background_area;
+           }
+
+         cell = column->cell;
+
+         if ((background_area.x > (gint) event->x) ||
+             (background_area.y > (gint) event->y) ||
+             (background_area.x + background_area.width <= (gint) event->x) ||
+             (background_area.y + background_area.height <= (gint) event->y))
+           {
+             background_area.x += background_area.width;
+             continue;
+           }
+
+         tree_node = gtk_tree_model_get_node (tree_view->priv->model,
+                                              path);
+         gtk_tree_view_column_set_cell_data (column,
+                                             tree_view->priv->model,
+                                             tree_node);
+
+         path_string = gtk_tree_path_to_string (path);
+         if (gtk_cell_renderer_event (cell,
+                                      (GdkEvent *)event,
+                                      widget,
+                                      path_string,
+                                      &background_area,
+                                      &cell_area,
+                                      0))
+
+           {
+             g_free (path_string);
+             gtk_tree_path_free (path);
+             return TRUE;
+           }
+         else
+           {
+             g_free (path_string);
+             break;
+           }
+       }
+      /* Handle the selection */
+      if (tree_view->priv->selection == NULL)
+       gtk_tree_selection_new_with_tree_view (tree_view);
+
+      _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
+                                               node,
+                                               tree,
+                                               path,
+                                               event->state);
+      gtk_tree_path_free (path);
+      return TRUE;
+    }
+
+  for (i = 0, list = tree_view->priv->column; list; list = list->next, i++)
+    {
+      column = list->data;
+      if (event->window == column->window &&
+         column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE &&
+         column->window)
+       {
+         gpointer drag_data;
+
+         if (gdk_pointer_grab (column->window, FALSE,
+                               GDK_POINTER_MOTION_HINT_MASK |
+                               GDK_BUTTON1_MOTION_MASK |
+                               GDK_BUTTON_RELEASE_MASK,
+                               NULL, NULL, event->time))
+           return FALSE;
+
+         gtk_grab_add (widget);
+         GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
+
+         /* block attached dnd signal handler */
+         drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
+         if (drag_data)
+           gtk_signal_handler_block_by_data (GTK_OBJECT (widget), drag_data);
+
+         if (!GTK_WIDGET_HAS_FOCUS (widget))
+           gtk_widget_grab_focus (widget);
+
+         tree_view->priv->drag_pos = i;
+         tree_view->priv->x_drag = (column->button->allocation.x + column->button->allocation.width);
+       }
+    }
+  return TRUE;
+}
+
+static gboolean
+gtk_tree_view_button_release (GtkWidget      *widget,
+                             GdkEventButton *event)
+{
+  GtkTreeView *tree_view;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+    {
+      gpointer drag_data;
+      gint width;
+      gint x;
+      gint i;
+
+      i = tree_view->priv->drag_pos;
+      tree_view->priv->drag_pos = -1;
+
+      /* unblock attached dnd signal handler */
+      drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
+      if (drag_data)
+       gtk_signal_handler_unblock_by_data (GTK_OBJECT (widget), drag_data);
+
+      GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
+      gtk_widget_get_pointer (widget, &x, NULL);
+      gtk_grab_remove (widget);
+      gdk_pointer_ungrab (event->time);
+
+      width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), i, &x);
+      gtk_tree_view_column_set_size (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), i), width);
+      return FALSE;
+    }
+
+  if (tree_view->priv->button_pressed_node == NULL)
+    return FALSE;
+
+  if (event->button == 1)
+    {
+      gtk_grab_remove (widget);
+      if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+       {
+         GtkTreePath *path;
+         GtkTreeNode *tree_node;
+
+         /* Actually activate the node */
+         if (tree_view->priv->button_pressed_node->children == NULL)
+           {
+             path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget),
+                                              tree_view->priv->button_pressed_tree,
+                                              tree_view->priv->button_pressed_node);
+             tree_view->priv->button_pressed_node->children = _gtk_rbtree_new ();
+             tree_view->priv->button_pressed_node->children->parent_tree = tree_view->priv->button_pressed_tree;
+             tree_view->priv->button_pressed_node->children->parent_node = tree_view->priv->button_pressed_node;
+             tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+             tree_node = gtk_tree_model_node_children (tree_view->priv->model, tree_node);
+
+             gtk_tree_view_build_tree (tree_view,
+                                       tree_view->priv->button_pressed_node->children,
+                                       tree_node,
+                                       gtk_tree_path_get_depth (path) + 1,
+                                       FALSE,
+                                       GTK_WIDGET_REALIZED (widget));
+           }
+         else
+           {
+             path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget),
+                                              tree_view->priv->button_pressed_node->children,
+                                              tree_view->priv->button_pressed_node->children->root);
+             tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+
+             gtk_tree_view_discover_dirty (GTK_TREE_VIEW (widget),
+                                           tree_view->priv->button_pressed_node->children,
+                                           tree_node,
+                                           gtk_tree_path_get_depth (path));
+             _gtk_rbtree_remove (tree_view->priv->button_pressed_node->children);
+           }
+         gtk_tree_path_free (path);
+
+         _gtk_tree_view_set_size (GTK_TREE_VIEW (widget), -1, -1);
+         gtk_widget_queue_resize (widget);
+       }
+
+      tree_view->priv->button_pressed_node = NULL;
+    }
+
+  return TRUE;
+}
+
+
+static void
+gtk_tree_view_draw_focus (GtkWidget *widget)
+{
+  GtkTreeView *tree_view;
+  GtkRBTree *cursor_tree = NULL;
+  GtkRBNode *cursor = NULL;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS))
+    return;
+  if (tree_view->priv->cursor == NULL)
+    return;
+
+  _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor);
+  if (cursor == NULL)
+    return;
+
+  gdk_draw_rectangle (tree_view->priv->bin_window,
+                     widget->style->fg_gc[GTK_STATE_NORMAL],
+                     FALSE,
+                     0,
+                     _gtk_rbtree_node_find_offset (cursor_tree, cursor) + TREE_VIEW_HEADER_HEIGHT (tree_view),
+                     (gint) MAX (tree_view->priv->width, tree_view->priv->hadjustment->upper),
+                     GTK_RBNODE_GET_HEIGHT (cursor));
+}
+
+
+static gint
+gtk_tree_view_focus_in (GtkWidget     *widget,
+                       GdkEventFocus *event)
+{
+  GtkTreeView *tree_view;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+  gtk_widget_draw_focus (widget);
+
+  return FALSE;
+}
+
+
+static gint
+gtk_tree_view_focus_out (GtkWidget     *widget,
+                        GdkEventFocus *event)
+{
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+  gtk_widget_queue_draw (widget);
+
+  return FALSE;
+}
+
+/* FIXME: It would be neat to someday make the headers a seperate widget that
+ * can be shared between various apps
+ */
+/* Returns TRUE if the focus is within the headers, after the focus operation is
+ * done
+ */
+static gboolean
+gtk_tree_view_header_focus (GtkTreeView        *tree_view,
+                           GtkDirectionType  dir)
+{
+  GtkWidget *focus_child;
+  GtkContainer *container;
+
+  GList *last_column, *first_column;
+  GList *tmp_list;
+
+  if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+    return FALSE;
+
+  focus_child = GTK_CONTAINER (tree_view)->focus_child;
+  container = GTK_CONTAINER (tree_view);
+
+  for (last_column = g_list_last (tree_view->priv->column);
+       last_column &&
+        !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) &&
+        GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button);
+       last_column = last_column->prev)
+    ;
+
+  for (first_column = tree_view->priv->column;
+       first_column &&
+        !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible) &&
+        GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (first_column->data)->button);
+       first_column = first_column->next)
+    ;
+
+  /* no headers are visible, or are focussable.  We can't focus in or out.
+   * I wonder if focussable is a real word...
+   */
+  if (last_column == NULL)
+    {
+      gtk_container_set_focus_child (container, NULL);
+      return FALSE;
+    }
+
+  /* First thing we want to handle is entering and leaving the headers.
+   */
+  switch (dir)
+    {
+    case GTK_DIR_TAB_BACKWARD:
+      if (!focus_child)
+       {
+         focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
+         gtk_container_set_focus_child (container, focus_child);
+         gtk_widget_grab_focus (focus_child);
+         goto cleanup;
+       }
+      if (focus_child == GTK_TREE_VIEW_COLUMN (first_column->data)->button)
+       {
+         focus_child = NULL;
+         goto cleanup;
+       }
+      break;
+
+    case GTK_DIR_TAB_FORWARD:
+      if (!focus_child)
+       {
+         focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+         gtk_container_set_focus_child (container, focus_child);
+         gtk_widget_grab_focus (focus_child);
+         goto cleanup;
+       }
+      if (focus_child == GTK_TREE_VIEW_COLUMN (last_column->data)->button)
+       {
+         focus_child = NULL;
+         goto cleanup;
+       }
+      break;
+
+    case GTK_DIR_LEFT:
+      if (!focus_child)
+       {
+         focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
+         gtk_container_set_focus_child (container, focus_child);
+         gtk_widget_grab_focus (focus_child);
+         goto cleanup;
+       }
+      if (focus_child == GTK_TREE_VIEW_COLUMN (first_column->data)->button)
+       {
+         focus_child = NULL;
+         goto cleanup;
+       }
+      break;
+
+    case GTK_DIR_RIGHT:
+      if (!focus_child)
+       {
+         focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+         gtk_container_set_focus_child (container, focus_child);
+         gtk_widget_grab_focus (focus_child);
+         goto cleanup;
+       }
+      if (focus_child == GTK_TREE_VIEW_COLUMN (last_column->data)->button)
+       {
+         focus_child = NULL;
+         goto cleanup;
+       }
+      break;
+
+    case GTK_DIR_UP:
+      if (!focus_child)
+       {
+         focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+         gtk_container_set_focus_child (container, focus_child);
+         gtk_widget_grab_focus (focus_child);
+       }
+      else
+       {
+         focus_child = NULL;
+       }
+      goto cleanup;
+
+    case GTK_DIR_DOWN:
+      if (!focus_child)
+       {
+         focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+         gtk_container_set_focus_child (container, focus_child);
+         gtk_widget_grab_focus (focus_child);
+       }
+      else
+       {
+         focus_child = NULL;
+       }
+      goto cleanup;
+    }
+
+  /* We need to move the focus to the next button. */
+  if (focus_child)
+    {
+      for (tmp_list = tree_view->priv->column; tmp_list; tmp_list = tmp_list->next)
+       if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
+         {
+           if (gtk_container_focus (GTK_CONTAINER (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button), dir))
+             {
+               /* The focus moves inside the button. */
+               /* This is probably a great example of bad UI */
+               goto cleanup;
+             }
+           break;
+         }
+
+      /* We need to move the focus among the row of buttons. */
+      while (tmp_list)
+       {
+         GtkTreeViewColumn *column;
+
+         if (dir == GTK_DIR_RIGHT || dir == GTK_DIR_TAB_FORWARD)
+           tmp_list = tmp_list->next;
+         else
+           tmp_list = tmp_list->prev;
+
+         if (tmp_list == NULL)
+           {
+             g_warning ("Internal button not found");
+             goto cleanup;
+           }
+         column = tmp_list->data;
+         if (column->button &&
+             column->visible &&
+             GTK_WIDGET_CAN_FOCUS (column->button))
+           {
+             focus_child = column->button;
+             gtk_container_set_focus_child (container, column->button);
+             gtk_widget_grab_focus (column->button);
+             break;
+           }
+       }
+    }
+
+ cleanup:
+  /* if focus child is non-null, we assume it's been set to the current focus child
+   */
+  if (focus_child)
+    {
+      /* If the following isn't true, then the view is smaller then the scrollpane.
+       */
+      if ((focus_child->allocation.x + focus_child->allocation.width) <=
+         (tree_view->priv->hadjustment->upper))
+       {
+         /* Scroll to the button, if needed */
+         if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) <
+             (focus_child->allocation.x + focus_child->allocation.width))
+           gtk_adjustment_set_value (tree_view->priv->hadjustment,
+                                     focus_child->allocation.x + focus_child->allocation.width -
+                                     tree_view->priv->hadjustment->page_size);
+         else if (tree_view->priv->hadjustment->value > focus_child->allocation.x)
+           gtk_adjustment_set_value (tree_view->priv->hadjustment,
+                                     focus_child->allocation.x);
+       }
+    }
+  else
+    {
+      gtk_container_set_focus_child (container, NULL);
+    }
+
+  return (focus_child != NULL);
+}
+
+/* WARNING: Scary function */
+static gint
+gtk_tree_view_focus (GtkContainer     *container,
+                    GtkDirectionType  direction)
+{
+  GtkTreeView *tree_view;
+  GtkWidget *focus_child;
+  GdkEvent *event;
+  GtkRBTree *cursor_tree;
+  GtkRBNode *cursor_node;
+
+  g_return_val_if_fail (container != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (container), FALSE);
+  g_return_val_if_fail (GTK_WIDGET_VISIBLE (container), FALSE);
+
+  tree_view = GTK_TREE_VIEW (container);
+
+  if (!GTK_WIDGET_IS_SENSITIVE (container))
+    return FALSE;
+  if (tree_view->priv->tree == NULL)
+    return FALSE;
+
+  focus_child = container->focus_child;
+
+  /* Case 1.  Headers have focus. */
+  if (focus_child)
+    {
+      switch (direction)
+       {
+       case GTK_DIR_LEFT:
+       case GTK_DIR_TAB_BACKWARD:
+         return (gtk_tree_view_header_focus (tree_view, direction));
+       case GTK_DIR_UP:
+         gtk_container_set_focus_child (container, NULL);
+         return FALSE;
+       case GTK_DIR_TAB_FORWARD:
+       case GTK_DIR_RIGHT:
+       case GTK_DIR_DOWN:
+         if (direction == GTK_DIR_DOWN)
+           {
+             gtk_container_set_focus_child (container, NULL);
+           }
+         else
+           {
+             if (gtk_tree_view_header_focus (tree_view, direction))
+               return TRUE;
+           }
+         GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+         gtk_widget_grab_focus (GTK_WIDGET (container));
+
+         if (tree_view->priv->selection == NULL)
+           gtk_tree_selection_new_with_tree_view (tree_view);
+
+         /* if there is no keyboard focus yet, we select the first node
+          */
+         if (tree_view->priv->cursor == NULL)
+           tree_view->priv->cursor = gtk_tree_path_new_root ();
+         if (tree_view->priv->cursor)
+           gtk_tree_selection_select_path (tree_view->priv->selection,
+                                           tree_view->priv->cursor);
+         gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+         return TRUE;
+       }
+    }
+
+  /* Case 2. We don't have focus at all. */
+  if (!GTK_WIDGET_HAS_FOCUS (container))
+    {
+      if ((direction == GTK_DIR_TAB_FORWARD) ||
+         (direction == GTK_DIR_RIGHT) ||
+         (direction == GTK_DIR_DOWN))
+       {
+         if (gtk_tree_view_header_focus (tree_view, direction))
+           return TRUE;
+       }
+
+      /* The headers didn't want the focus, so we take it. */
+      GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+      gtk_widget_grab_focus (GTK_WIDGET (container));
+
+      if (tree_view->priv->selection == NULL)
+       gtk_tree_selection_new_with_tree_view (tree_view);
+
+      if (tree_view->priv->cursor == NULL)
+       tree_view->priv->cursor = gtk_tree_path_new_root ();
+
+      if (tree_view->priv->cursor)
+       gtk_tree_selection_select_path (tree_view->priv->selection,
+                                       tree_view->priv->cursor);
+      gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+      return TRUE;
+    }
+
+  /* Case 3. We have focus already, but no cursor.  We pick the first one
+   * and run with it. */
+  if (tree_view->priv->cursor == NULL)
+    {
+      /* We lost our cursor somehow.  Arbitrarily select the first node, and
+       * return
+       */
+      tree_view->priv->cursor = gtk_tree_path_new_root ();
+
+      if (tree_view->priv->cursor)
+       gtk_tree_selection_select_path (tree_view->priv->selection,
+                                       tree_view->priv->cursor);
+      gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
+                               0.0);
+      gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+      return TRUE;
+    }
+
+
+  /* Case 3. We have focus already.  Move the cursor. */
+  if (direction == GTK_DIR_LEFT)
+    {
+      gfloat val;
+      val = tree_view->priv->hadjustment->value - tree_view->priv->hadjustment->page_size/2;
+      val = MAX (val, 0.0);
+      gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val);
+      gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+      return TRUE;
+    }
+  if (direction == GTK_DIR_RIGHT)
+    {
+      gfloat val;
+      val = tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size/2;
+      val = MIN (tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size, val);
+      gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val);
+      gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+      return TRUE;
+    }
+  cursor_tree = NULL;
+  cursor_node = NULL;
+
+  _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor,
+                           &cursor_tree,
+                           &cursor_node);
+  switch (direction)
+    {
+    case GTK_DIR_TAB_BACKWARD:
+    case GTK_DIR_UP:
+      _gtk_rbtree_prev_full (cursor_tree,
+                            cursor_node,
+                            &cursor_tree,
+                            &cursor_node);
+      break;
+    case GTK_DIR_TAB_FORWARD:
+    case GTK_DIR_DOWN:
+      _gtk_rbtree_next_full (cursor_tree,
+                            cursor_node,
+                            &cursor_tree,
+                            &cursor_node);
+      break;
+    default:
+      break;
+    }
+
+  if (cursor_node)
+    {
+      GdkModifierType state = 0;
+
+      event = gdk_event_peek ();
+      if (event && event->type == GDK_KEY_PRESS)
+       /* FIXME: This doesn't seem to work. )-:
+        * I fear the event may already have been gotten */
+       state = ((GdkEventKey *)event)->state;
+
+      if (event)
+       gdk_event_free (event);
+      gtk_tree_path_free (tree_view->priv->cursor);
+
+      tree_view->priv->cursor = _gtk_tree_view_find_path (tree_view,
+                                                         cursor_tree,
+                                                         cursor_node);
+      if (tree_view->priv->cursor)
+       _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
+                                                 cursor_node,
+                                                 cursor_tree,
+                                                 tree_view->priv->cursor,
+                                                 state);
+      gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
+      gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+      gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+      return TRUE;
+    }
+
+  /* At this point, we've progressed beyond the edge of the rows. */
+
+  if ((direction == GTK_DIR_LEFT) ||
+      (direction == GTK_DIR_TAB_BACKWARD) ||
+      (direction == GTK_DIR_UP))
+    /* We can't go back anymore.  Try the headers */
+    return (gtk_tree_view_header_focus (tree_view, direction));
+
+  /* we've reached the end of the tree.  Go on. */
+  return FALSE;
+}
+
+/* Container method
+ */
+static void
+gtk_tree_view_remove (GtkContainer *container,
+                     GtkWidget    *widget)
+{
+  GtkTreeView *tree_view;
+  GtkTreeViewChild *child = NULL;
+  GList *tmp_list;
+
+  g_return_if_fail (container != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (container));
+
+  tree_view = GTK_TREE_VIEW (container);
+
+  tmp_list = tree_view->priv->children;
+  while (tmp_list)
+    {
+      child = tmp_list->data;
+      if (child->widget == widget)
+       break;
+      tmp_list = tmp_list->next;
+    }
+
+  if (tmp_list)
+    {
+      gtk_widget_unparent (widget);
+
+      tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
+      g_list_free_1 (tmp_list);
+      g_free (child);
+    }
+}
+
+static void
+gtk_tree_view_forall (GtkContainer *container,
+                     gboolean      include_internals,
+                     GtkCallback   callback,
+                     gpointer      callback_data)
+{
+  GtkTreeView *tree_view;
+  GtkTreeViewChild *child = NULL;
+  GtkTreeViewColumn *column;
+  GList *tmp_list;
+
+  g_return_if_fail (container != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (container));
+  g_return_if_fail (callback != NULL);
+
+  tree_view = GTK_TREE_VIEW (container);
+
+  tmp_list = tree_view->priv->children;
+  while (tmp_list)
+    {
+      child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      (* callback) (child->widget, callback_data);
+    }
+  if (include_internals == FALSE)
+    return;
+
+  for (tmp_list = tree_view->priv->column; tmp_list; tmp_list = tmp_list->next)
+    {
+      column = tmp_list->data;
+      if (column->button)
+       (* callback) (column->button, callback_data);
+    }
+}
+
+/* TreeModel Methods
+ */
+
+static void
+gtk_tree_view_node_changed (GtkTreeModel *model,
+                           GtkTreePath  *path,
+                           GtkTreeNode  *tree_node,
+                           gpointer      data)
+{
+  GtkTreeView *tree_view = (GtkTreeView *)data;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+  gint height;
+  gboolean dirty_marked;
+
+  g_return_if_fail (path != NULL || node != NULL);
+
+  if (path == NULL)
+    path = gtk_tree_model_get_path (model, tree_node);
+  else if (tree_node == NULL)
+    tree_node = gtk_tree_model_get_node (model, path);
+
+  if (_gtk_tree_view_find_node (tree_view,
+                               path,
+                               &tree,
+                               &node))
+    /* We aren't actually showing the node */
+    return;
+
+  dirty_marked = gtk_tree_view_discover_dirty_node (tree_view,
+                                                   tree_node,
+                                                   gtk_tree_path_get_depth (path),
+                                                   &height);
+
+  if (GTK_RBNODE_GET_HEIGHT (node) != height + TREE_VIEW_VERTICAL_SEPERATOR)
+    {
+      _gtk_rbtree_node_set_height (tree, node, height + TREE_VIEW_VERTICAL_SEPERATOR);
+      gtk_widget_queue_resize (GTK_WIDGET (data));
+      return;
+    }
+  if (dirty_marked)
+    gtk_widget_queue_resize (GTK_WIDGET (data));
+  else
+    {
+      /* FIXME: just redraw the node */
+      gtk_widget_queue_resize (GTK_WIDGET (data));
+    }
+}
+
+static void
+gtk_tree_view_node_inserted (GtkTreeModel *model,
+                            GtkTreePath  *path,
+                            GtkTreeNode  *tree_node,
+                            gpointer      data)
+{
+  GtkTreeView *tree_view = (GtkTreeView *) data;
+  gint *indices;
+  GtkRBTree *tmptree, *tree;
+  GtkRBNode *tmpnode = NULL;
+  gint max_height;
+  gint depth;
+  gint i = 0;
+
+  tmptree = tree = tree_view->priv->tree;
+  g_return_if_fail (path != NULL || tree_node != NULL);
+
+  if (path == NULL)
+    path = gtk_tree_model_get_path (model, tree_node);
+  else if (tree_node == NULL)
+    tree_node = gtk_tree_model_get_node (model, path);
+
+  depth = gtk_tree_path_get_depth (path);
+  indices = gtk_tree_path_get_indices (path);
+
+  /* First, find the parent tree */
+  while (i < depth - 1)
+    {
+      if (tmptree == NULL)
+       {
+         /* We aren't showing the node */
+         return;
+       }
+
+      tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
+      if (tmpnode == NULL)
+       {
+         g_warning ("A node was inserted with a parent that's not in the tree.\n" \
+                    "This possibly means that a GtkTreeModel inserted a child node\n" \
+                    "before the parent was inserted.");
+         return;
+       }
+      else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT))
+       {
+         /* In theory, the model should have emitted child_toggled here.  We
+          * try to catch it anyway, just to be safe, in case the model hasn't.
+          */
+         GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view,
+                                                          tree,
+                                                          tmpnode);
+         gtk_tree_view_node_child_toggled (model, tmppath, NULL, data);
+         gtk_tree_path_free (tmppath);
+         return;
+       }
+
+      tmptree = tmpnode->children;
+      tree = tmptree;
+      i++;
+    }
+
+  if (tree == NULL)
+    return;
+
+  /* next, update the selection */
+  if (tree_view->priv->anchor)
+    {
+      gint *select_indices = gtk_tree_path_get_indices (tree_view->priv->anchor);
+      gint select_depth = gtk_tree_path_get_depth (tree_view->priv->anchor);
+
+      for (i = 0; i < depth && i < select_depth; i++)
+       {
+         if (indices[i] < select_indices[i])
+           {
+             select_indices[i]++;
+             break;
+           }
+         else if (indices[i] > select_indices[i])
+           break;
+         else if (i == depth - 1)
+           {
+             select_indices[i]++;
+             break;
+           }
+       }
+    }
+
+  max_height = gtk_tree_view_insert_node_height (tree_view,
+                                                tree,
+                                                tree_node,
+                                                depth);
+  if (indices[depth - 1] == 0)
+    {
+      tmpnode = _gtk_rbtree_find_count (tree, 1);
+      _gtk_rbtree_insert_before (tree, tmpnode, max_height);
+    }
+  else
+    {
+      tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]);
+      _gtk_rbtree_insert_after (tree, tmpnode, max_height);
+    }
+
+  _gtk_tree_view_set_size (tree_view, -1, tree_view->priv->height + max_height);
+}
+
+static void
+gtk_tree_view_node_child_toggled (GtkTreeModel *model,
+                                 GtkTreePath  *path,
+                                 GtkTreeNode  *tree_node,
+                                 gpointer      data)
+{
+  GtkTreeView *tree_view = (GtkTreeView *)data;
+  gboolean has_child;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+
+  g_return_if_fail (path != NULL || node != NULL);
+
+  if (path == NULL)
+    path = gtk_tree_model_get_path (model, tree_node);
+  else if (tree_node == NULL)
+    tree_node = gtk_tree_model_get_node (model, path);
+
+  if (_gtk_tree_view_find_node (tree_view,
+                               path,
+                               &tree,
+                               &node))
+    /* We aren't actually showing the node */
+    return;
+
+  has_child = gtk_tree_model_node_has_child (model, tree_node);
+  /* Sanity check.
+   */
+  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child)
+    return;
+
+  if (has_child)
+    GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT);
+  else
+    GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
+
+  if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
+    {
+      GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
+      if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
+       {
+         GList *list;
+         for (list = tree_view->priv->column; list; list = list->next)
+           if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
+             {
+               GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE;
+               break;
+             }
+       }
+      gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+    }
+  else
+    {
+      /* FIXME: Just redraw the node */
+      gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+    }
+}
+
+static void
+gtk_tree_view_node_deleted (GtkTreeModel *model,
+                           GtkTreePath  *path,
+                           gpointer      data)
+{
+  GtkTreeView *tree_view = (GtkTreeView *)data;
+  GtkRBTree *tree;
+  GtkRBNode *node;
+  GList *list;
+
+  g_return_if_fail (path != NULL);
+
+  if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
+    return;
+
+  /* next, update the selection */
+#if 0
+  if (tree_view->priv->anchor)
+    {
+      gint i;
+      gint *select_indices = gtk_tree_path_get_indices (tree_view->priv->anchor);
+      gint select_depth = gtk_tree_path_get_depth (tree_view->priv->anchor);
+
+      if (gtk_tree_path_compare (path, tree_view->priv->anchor) == 0)
+       {
+
+       }
+      else
+       {
+         for (i = 0; i < depth && i < select_depth; i++)
+           {
+             if (indices[i] < select_indices[i])
+               {
+                 select_indices[i] = MAX (select_indices[i], 0);
+                 break;
+               }
+             else if (indices[i] > select_indices[i])
+               break;
+             else if (i == depth - 1)
+               {
+                 select_indices[i] = MAX (select_indices[i], 0);
+                 break;
+               }
+           }
+       }
+    }
+#endif
+
+  for (list = tree_view->priv->column; list; list = list->next)
+    if (((GtkTreeViewColumn *)list->data)->visible &&
+       ((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+      ((GtkTreeViewColumn *)list->data)->dirty = TRUE;
+
+  if (tree->root->count == 1)
+    _gtk_rbtree_remove (tree);
+  else
+    _gtk_rbtree_remove_node (tree, node);
+
+  _gtk_tree_view_set_size (GTK_TREE_VIEW (data), -1, -1);
+  gtk_widget_queue_resize (data);
+}
+
+/* Internal tree functions */
+static gint
+gtk_tree_view_insert_node_height (GtkTreeView *tree_view,
+                                 GtkRBTree   *tree,
+                                 GtkTreeNode  node,
+                                 gint         depth)
+{
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+  gboolean first = TRUE;
+  GList *list;
+  gint max_height = 0;
+
+  /* do stuff with node */
+  for (list = tree_view->priv->column; list; list = list->next)
+    {
+      gint height = 0, width = 0;
+      column = list->data;
+
+      if (!column->visible)
+       continue;
+      if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+       {
+         first = FALSE;;
+         continue;
+       }
+
+      cell = column->cell;
+      gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, node);
+
+      gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height);
+      max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPERATOR + height);
+
+      if (first == TRUE && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+       column->size = MAX (column->size, depth * tree_view->priv->tab_offset + width);
+      else
+       column->size = MAX (column->size, width);
+
+      first = FALSE;
+    }
+  return max_height;
+
+}
+
+static void
+gtk_tree_view_build_tree (GtkTreeView *tree_view,
+                         GtkRBTree   *tree,
+                         GtkTreeNode  node,
+                         gint         depth,
+                         gboolean     recurse,
+                         gboolean     calc_bounds)
+{
+  GtkRBNode *temp = NULL;
+  GtkTreeNode child;
+  gint max_height;
+
+  if (!node)
+    return;
+  do
+    {
+      max_height = 0;
+      if (calc_bounds)
+       max_height = gtk_tree_view_insert_node_height (tree_view,
+                                                      tree,
+                                                      node,
+                                                      depth);
+      temp = _gtk_rbtree_insert_after (tree, temp, max_height);
+      if (recurse)
+       {
+         child = gtk_tree_model_node_children (tree_view->priv->model, node);
+         if (child != NULL)
+           {
+             temp->children = _gtk_rbtree_new ();
+             temp->children->parent_tree = tree;
+             temp->children->parent_node = temp;
+             gtk_tree_view_build_tree (tree_view, temp->children, child, depth + 1, recurse, calc_bounds);
+           }
+       }
+      if (gtk_tree_model_node_has_child (tree_view->priv->model, node))
+       {
+         if ((temp->flags&GTK_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT)
+           temp->flags ^= GTK_RBNODE_IS_PARENT;
+         GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
+       }
+    }
+  while (gtk_tree_model_node_next (tree_view->priv->model, &node));
+}
+
+static void
+gtk_tree_view_calc_size (GtkTreeView *tree_view,
+                        GtkRBTree   *tree,
+                        GtkTreeNode  node,
+                        gint         depth)
+{
+  GtkRBNode *temp = tree->root;
+  GtkTreeNode child;
+  GtkCellRenderer *cell;
+  GList *list;
+  GtkTreeViewColumn *column;
+  gint max_height;
+  gint i;
+
+  /* FIXME: Make this function robust against internal inconsistencies! */
+  if (!node)
+    return;
+  TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL);
+
+  while (temp->left != tree->nil)
+    temp = temp->left;
+
+  do
+    {
+      max_height = 0;
+      /* Do stuff with node */
+      for (list = tree_view->priv->column, i = 0; i < tree_view->priv->columns; list = list->next, i++)
+       {
+         gint height = 0, width = 0;
+         column = list->data;
+
+         if (!column->visible)
+           continue;
+
+         gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, node);
+         cell = column->cell;
+         gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height);
+         max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPERATOR + height);
+
+         /* FIXME: I'm getting the width of all nodes here. )-: */
+         if (column->dirty == FALSE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+           continue;
+
+         if (i == 0 && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+           column->size = MAX (column->size, depth * tree_view->priv->tab_offset + width);
+         else
+           column->size = MAX (column->size, width);
+       }
+      _gtk_rbtree_node_set_height (tree, temp, max_height);
+      child = gtk_tree_model_node_children (tree_view->priv->model, node);
+      if (child != NULL && temp->children != NULL)
+       gtk_tree_view_calc_size (tree_view, temp->children, child, depth + 1);
+      temp = _gtk_rbtree_next (tree, temp);
+    }
+  while (gtk_tree_model_node_next (tree_view->priv->model, &node));
+}
+
+static gboolean
+gtk_tree_view_discover_dirty_node (GtkTreeView *tree_view,
+                                  GtkTreeNode  node,
+                                  gint         depth,
+                                  gint        *height)
+{
+  GtkCellRenderer *cell;
+  GtkTreeViewColumn *column;
+  GList *list;
+  gint i;
+  gint retval = FALSE;
+  gint tmpheight;
+
+  /* Do stuff with node */
+  if (height)
+    *height = 0;
+
+  for (i = 0, list = tree_view->priv->column; list; list = list->next, i++)
+    {
+      gint width;
+      column = list->data;
+      if (column->dirty == TRUE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+       continue;
+      if (!column->visible)
+       continue;
+
+      cell = column->cell;
+      gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, node);
+
+      if (height)
+       {
+         gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &tmpheight);
+         *height = MAX (*height, tmpheight);
+       }
+      else
+       {
+         gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, NULL);
+       }
+      if (i == 0 && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+       {
+         if (depth * tree_view->priv->tab_offset + width > column->size)
+           {
+             column->dirty = TRUE;
+             retval = TRUE;
+           }
+       }
+      else
+       {
+         if (width > column->size)
+           {
+             column->dirty = TRUE;
+             retval = TRUE;
+           }
+       }
+    }
+
+  return retval;
+}
+
+static void
+gtk_tree_view_discover_dirty (GtkTreeView *tree_view,
+                             GtkRBTree   *tree,
+                             GtkTreeNode  node,
+                             gint       depth)
+{
+  GtkRBNode *temp = tree->root;
+  GtkTreeViewColumn *column;
+  GList *list;
+  GtkTreeNode child;
+  gboolean is_all_dirty;
+
+  /* FIXME: Make this function robust against internal inconsistencies! */
+  if (!node)
+    return;
+  TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL);
+
+  while (temp->left != tree->nil)
+    temp = temp->left;
+
+  do
+    {
+      is_all_dirty = TRUE;
+      for (list = tree_view->priv->column; list; list = list->next)
+       {
+         column = list->data;
+         if (column->dirty == FALSE)
+           {
+             is_all_dirty = FALSE;
+             break;
+           }
+       }
+      if (is_all_dirty)
+       return;
+
+      gtk_tree_view_discover_dirty_node (tree_view,
+                                        node,
+                                        depth,
+                                        FALSE);
+      child = gtk_tree_model_node_children (tree_view->priv->model, node);
+      if (child != NULL && temp->children != NULL)
+       gtk_tree_view_discover_dirty (tree_view, temp->children, child, depth + 1);
+      temp = _gtk_rbtree_next (tree, temp);
+    }
+  while (gtk_tree_model_node_next (tree_view->priv->model, &node));
+}
+
+
+static void
+gtk_tree_view_check_dirty (GtkTreeView *tree_view)
+{
+  GtkTreePath *path;
+  GtkTreeNode *tree_node;
+  gboolean dirty = FALSE;
+  GList *list;
+  GtkTreeViewColumn *column;
+
+  for (list = tree_view->priv->column; list; list = list->next)
+    {
+      column = list->data;
+      if (column->dirty)
+       {
+         dirty = TRUE;
+         if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+           {
+             column->size = column->button->requisition.width;
+           }
+       }
+    }
+  if (dirty == FALSE)
+    return;
+
+  path = gtk_tree_path_new_root ();
+  if (path != NULL)
+    {
+      tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+      gtk_tree_path_free (path);
+      gtk_tree_view_calc_size (tree_view, tree_view->priv->tree, tree_node, 1);
+      _gtk_tree_view_set_size (tree_view, -1, -1);
+    }
+
+  for (list = tree_view->priv->column; list; list = list->next)
+    {
+      column = list->data;
+      column->dirty = FALSE;
+    }
+}
+
+static void
+gtk_tree_view_create_button (GtkTreeView *tree_view,
+                            gint       i)
+{
+  GtkWidget *button;
+  GtkTreeViewColumn *column;
+
+  column = g_list_nth (tree_view->priv->column, i)->data;
+  gtk_widget_push_composite_child ();
+  button = column->button = gtk_button_new ();
+  gtk_widget_pop_composite_child ();
+
+  gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
+
+  gtk_signal_connect (GTK_OBJECT (button), "clicked",
+                     (GtkSignalFunc) gtk_tree_view_button_clicked,
+                     (gpointer) tree_view);
+}
+
+static void
+gtk_tree_view_create_buttons (GtkTreeView *tree_view)
+{
+  GtkWidget *alignment;
+  GtkWidget *label;
+  GtkRequisition requisition;
+  GList *list;
+  GtkTreeViewColumn *column;
+  gint i;
+
+  for (list = tree_view->priv->column, i = 0; list; list = list->next, i++)
+    {
+      column = list->data;
+
+      gtk_tree_view_create_button (tree_view, i);
+      switch (column->justification)
+       {
+       case GTK_JUSTIFY_LEFT:
+         alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
+         break;
+       case GTK_JUSTIFY_RIGHT:
+         alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+         break;
+       case GTK_JUSTIFY_CENTER:
+         alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+         break;
+       case GTK_JUSTIFY_FILL:
+       default:
+         alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+         break;
+       }
+      label = gtk_label_new (column->title);
+
+      gtk_container_add (GTK_CONTAINER (alignment), label);
+      gtk_container_add (GTK_CONTAINER (column->button), alignment);
+
+      gtk_widget_show (label);
+      gtk_widget_show (alignment);
+      gtk_widget_size_request (column->button, &requisition);
+
+      column->size = MAX (column->size, requisition.width);
+      tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
+    }
+  if (GTK_WIDGET_REALIZED (tree_view))
+    {
+      gtk_tree_view_realize_buttons (tree_view);
+      if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+       {
+         /* We need to do this twice, as we need to map
+          * all the buttons before we map the columns */
+         for (list = tree_view->priv->column; list; list = list->next)
+           {
+             column = list->data;
+             if (column->visible == FALSE)
+               continue;
+             gtk_widget_map (column->button);
+           }
+         for (list = tree_view->priv->column; list; list = list->next)
+           {
+             column = list->data;
+             if (column->visible == FALSE)
+               continue;
+             if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
+               {
+                 gdk_window_raise (column->window);
+                 gdk_window_show (column->window);
+               }
+             else
+               gdk_window_hide (column->window);
+           }
+       }
+    }
+}
+
+static void
+gtk_tree_view_button_clicked (GtkWidget *widget,
+                             gpointer   data)
+{
+  GList *list;
+  GtkTreeView *tree_view;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (data));
+
+  tree_view = GTK_TREE_VIEW (data);
+
+  /* find the column who's button was pressed */
+  for (list = tree_view->priv->column; list; list = list->next)
+    if (GTK_TREE_VIEW_COLUMN (list->data)->button == widget)
+      break;
+
+  //  gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
+}
+
+/* Make sure the node is visible vertically */
+static void
+gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
+                                 GtkRBTree   *tree,
+                                 GtkRBNode   *node)
+{
+  gint offset;
+
+  offset = _gtk_rbtree_node_find_offset (tree, node);
+
+  /* we reverse the order, b/c in the unusual case of the
+   * node's height being taller then the visible area, we'd rather
+   * have the node flush to the top
+   */
+  if (offset + GTK_RBNODE_GET_HEIGHT (node) >
+      tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size)
+    gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
+                             offset + GTK_RBNODE_GET_HEIGHT (node) -
+                             tree_view->priv->vadjustment->page_size);
+  if (offset < tree_view->priv->vadjustment->value)
+    gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
+                             offset);
+}
+
+/* This function could be more efficient.
+ * I'll optimize it if profiling seems to imply that
+ * it's important */
+GtkTreePath *
+_gtk_tree_view_find_path (GtkTreeView *tree_view,
+                         GtkRBTree   *tree,
+                         GtkRBNode   *node)
+{
+  GtkTreePath *path;
+  GtkRBTree *tmp_tree;
+  GtkRBNode *tmp_node, *last;
+  gint count;
+
+  path = gtk_tree_path_new ();
+
+  g_return_val_if_fail (node != NULL, path);
+  g_return_val_if_fail (node != tree->nil, path);
+
+  count = 1 + node->left->count;
+
+  last = node;
+  tmp_node = node->parent;
+  tmp_tree = tree;
+  while (tmp_tree)
+    {
+      while (tmp_node != tmp_tree->nil)
+       {
+         if (tmp_node->right == last)
+           count += 1 + tmp_node->left->count;
+         last = tmp_node;
+         tmp_node = tmp_node->parent;
+       }
+      gtk_tree_path_prepend_index (path, count - 1);
+      last = tmp_tree->parent_node;
+      tmp_tree = tmp_tree->parent_tree;
+      if (last)
+       {
+         count = 1 + last->left->count;
+         tmp_node = last->parent;
+       }
+    }
+  return path;
+}
+
+/* Returns wether or not it's a parent, or not */
+gboolean
+_gtk_tree_view_find_node (GtkTreeView  *tree_view,
+                         GtkTreePath  *path,
+                         GtkRBTree   **tree,
+                         GtkRBNode   **node)
+{
+  GtkRBNode *tmpnode = NULL;
+  GtkRBTree *tmptree = tree_view->priv->tree;
+  gint *indices = gtk_tree_path_get_indices (path);
+  gint depth = gtk_tree_path_get_depth (path);
+  gint i = 0;
+
+  *node = NULL;
+  *tree = NULL;
+
+  do
+    {
+      if (tmptree == NULL)
+       {
+         *node = tmpnode;
+         *tree = tmptree;
+         return TRUE;
+       }
+      tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
+      if (++i >= depth)
+       {
+         *node = tmpnode;
+         *tree = tmptree;
+         return FALSE;
+       }
+      tmptree = tmpnode->children;
+    }
+  while (1);
+}
+
+/* x and y are the mouse position
+ */
+static void
+gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
+                         GtkRBNode   *node,
+                         gint         offset,
+                         gint         x,
+                         gint         y)
+{
+  GdkRectangle area;
+  GtkStateType state;
+  GtkShadowType shadow;
+  GdkPoint points[3];
+
+  area.x = 0;
+  area.y = offset + TREE_VIEW_VERTICAL_SEPERATOR;
+  area.width = tree_view->priv->tab_offset - 2;
+  area.height = GTK_RBNODE_GET_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPERATOR;
+
+  if (node == tree_view->priv->button_pressed_node)
+    {
+      if (x >= area.x && x <= (area.x + area.width) &&
+         y >= area.y && y <= (area.y + area.height))
+       {
+         state = GTK_STATE_ACTIVE;
+         shadow = GTK_SHADOW_IN;
+       }
+      else
+       {
+         state = GTK_STATE_NORMAL;
+         shadow = GTK_SHADOW_OUT;
+       }
+    }
+  else
+    {
+      state = (node==tree_view->priv->prelight_node&&GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)?GTK_STATE_PRELIGHT:GTK_STATE_NORMAL);
+      shadow = GTK_SHADOW_OUT;
+    }
+
+  if (TRUE ||
+      (((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT) &&
+       node->children))
+    {
+      points[0].x = area.x + 2;
+      points[0].y = area.y + (area.height - TREE_VIEW_EXPANDER_HEIGHT)/2;
+      points[1].x = points[0].x + TREE_VIEW_EXPANDER_WIDTH/2;
+      points[1].y = points[0].y + TREE_VIEW_EXPANDER_HEIGHT/2;
+      points[2].x = points[0].x;
+      points[2].y = points[0].y + TREE_VIEW_EXPANDER_HEIGHT;
+
+    }
+
+  gdk_draw_polygon (tree_view->priv->bin_window,
+                   GTK_WIDGET (tree_view)->style->base_gc[state],
+                   TRUE, points, 3);
+  gdk_draw_polygon (tree_view->priv->bin_window,
+                   GTK_WIDGET (tree_view)->style->fg_gc[state],
+                   FALSE, points, 3);
+
+
+  /*    gtk_paint_arrow (GTK_WIDGET (tree_view)->style, */
+  /*              tree_view->priv->bin_window, */
+  /*              state, */
+  /*              shadow, */
+  /*              &area, */
+  /*              GTK_WIDGET (tree_view), */
+  /*              "GtkTreeView", */
+  /*              arrow_dir, */
+  /*              TRUE, */
+  /*              area.x, area.y, */
+  /*              area.width, area.height); */
+}
+
+void
+_gtk_tree_view_set_size (GtkTreeView     *tree_view,
+                        gint           width,
+                        gint           height)
+{
+  GList *list;
+  GtkTreeViewColumn *column;
+  gint i;
+
+  if (tree_view->priv->model == NULL)
+    {
+      tree_view->priv->width = 1;
+      tree_view->priv->height = 1;
+      return;
+    }
+  if (width == -1)
+    {
+      width = 0;
+      for (list = tree_view->priv->column, i = 0; list; list = list->next, i++)
+       {
+         column = list->data;
+         if (!column->visible)
+           continue;
+         width += TREE_VIEW_COLUMN_SIZE (column);
+       }
+    }
+  if (height == -1)
+    height = tree_view->priv->tree->root->offset + TREE_VIEW_VERTICAL_SEPERATOR;
+
+  tree_view->priv->width = width;
+  tree_view->priv->height = height;
+
+  if (tree_view->priv->hadjustment->upper != tree_view->priv->width)
+    {
+      tree_view->priv->hadjustment->upper = tree_view->priv->width;
+      gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed");
+    }
+
+  if (tree_view->priv->vadjustment->upper != tree_view->priv->height)
+    {
+      tree_view->priv->vadjustment->upper = tree_view->priv->height;
+      gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
+    }
+
+  if (GTK_WIDGET_REALIZED (tree_view))
+    {
+      gdk_window_resize (tree_view->priv->bin_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), height + TREE_VIEW_HEADER_HEIGHT (tree_view));
+      gdk_window_resize (tree_view->priv->header_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), tree_view->priv->header_height);
+    }
+  gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+}
+
+/* this function returns the new width of the column being resized given
+ * the column and x position of the cursor; the x cursor position is passed
+ * in as a pointer and automagicly corrected if it's beyond min/max limits */
+static gint
+gtk_tree_view_new_column_width (GtkTreeView *tree_view,
+                               gint       i,
+                               gint      *x)
+{
+  GtkTreeViewColumn *column;
+  gint width;
+
+  /* first translate the x position from widget->window
+   * to clist->clist_window */
+
+  column = g_list_nth (tree_view->priv->column, i)->data;
+  width = *x - column->button->allocation.x;
+
+  /* Clamp down the value */
+  if (column->min_width == -1)
+    width = MAX (column->button->requisition.width,
+                width);
+  else
+    width = MAX (column->min_width,
+                width);
+  if (column->max_width != -1)
+    width = MIN (width, column->max_width != -1);
+  *x = column->button->allocation.x + width;
+
+  return width;
+}
+
+/* Callbacks */
+static void
+gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
+                                 GtkTreeView     *tree_view)
+{
+  if (GTK_WIDGET_REALIZED (tree_view))
+    {
+      gdk_window_move (tree_view->priv->bin_window,
+                      - tree_view->priv->hadjustment->value,
+                      - tree_view->priv->vadjustment->value);
+      gdk_window_move (tree_view->priv->header_window,
+                      - tree_view->priv->hadjustment->value,
+                      0);
+
+      gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
+      gdk_window_process_updates (tree_view->priv->header_window, TRUE);
+    }
+}
+
+\f
+
+/* Public methods
+ */
+GtkWidget *
+gtk_tree_view_new (void)
+{
+  GtkTreeView *tree_view;
+
+  tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ()));
+
+  return GTK_WIDGET (tree_view);
+}
+
+GtkWidget *
+gtk_tree_view_new_with_model (GtkTreeModel *model)
+{
+  GtkTreeView *tree_view;
+
+  tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ()));
+  gtk_tree_view_set_model (tree_view, model);
+
+  return GTK_WIDGET (tree_view);
+}
+
+GtkTreeModel *
+gtk_tree_view_get_model (GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (tree_view != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+  return tree_view->priv->model;
+}
+
+static void
+gtk_tree_view_set_model_realized (GtkTreeView *tree_view)
+{
+  GtkTreePath *path;
+  GtkTreeNode  node;
+
+  tree_view->priv->tree = _gtk_rbtree_new ();
+
+  gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+                     "node_changed",
+                     gtk_tree_view_node_changed,
+                     tree_view);
+  gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+                     "node_inserted",
+                     gtk_tree_view_node_inserted,
+                     tree_view);
+  gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+                     "node_child_toggled",
+                     gtk_tree_view_node_child_toggled,
+                     tree_view);
+  gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+                     "node_deleted",
+                     gtk_tree_view_node_deleted,
+                     tree_view);
+
+  if (tree_view->priv->column == NULL)
+    return;
+
+  path = gtk_tree_path_new_root ();
+  if (path == NULL)
+    return;
+
+  node = gtk_tree_model_get_node (tree_view->priv->model, path);
+  gtk_tree_path_free (path);
+  gtk_tree_view_build_tree (tree_view, tree_view->priv->tree, node, 1, FALSE, GTK_WIDGET_REALIZED (tree_view));
+
+  gtk_tree_view_create_buttons (tree_view);
+  GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP);
+}
+
+void
+gtk_tree_view_set_model (GtkTreeView *tree_view, GtkTreeModel *model)
+{
+  GList *list;
+  GtkTreeViewColumn *column;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  if (tree_view->priv->model != NULL)
+    {
+      for (list = tree_view->priv->column; list; list = list->next)
+       {
+         column = list->data;
+         if (column->button)
+           {
+             gtk_widget_unparent (column->button);
+             gdk_window_set_user_data (column->window, NULL);
+             gdk_window_destroy (column->window);
+           }
+       }
+      if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP))
+       {
+         gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+                                        gtk_tree_view_node_changed,
+                                        tree_view);
+         gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+                                        gtk_tree_view_node_inserted,
+                                        tree_view);
+         gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+                                        gtk_tree_view_node_child_toggled,
+                                        tree_view);
+         gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+                                        gtk_tree_view_node_deleted,
+                                        tree_view);
+         _gtk_rbtree_free (tree_view->priv->tree);
+       }
+
+      g_list_free (tree_view->priv->column);
+      tree_view->priv->column = NULL;
+      GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP);
+    }
+
+  tree_view->priv->model = model;
+  if (model == NULL)
+    {
+      tree_view->priv->tree = NULL;
+      tree_view->priv->columns = 0;
+      tree_view->priv->column = NULL;
+      if (GTK_WIDGET_REALIZED (tree_view))
+       _gtk_tree_view_set_size (tree_view, 0, 0);
+      return;
+    }
+
+  if (GTK_WIDGET_REALIZED (tree_view))
+    {
+      gtk_tree_view_set_model_realized (tree_view);
+      _gtk_tree_view_set_size (tree_view, -1, -1);
+    }
+}
+
+GtkTreeSelection *
+gtk_tree_view_get_selection (GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (tree_view != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+  if (tree_view->priv->selection == NULL)
+    gtk_tree_selection_new_with_tree_view (tree_view);
+
+  return tree_view->priv->selection;
+}
+
+void
+gtk_tree_view_set_selection (GtkTreeView      *tree_view,
+                            GtkTreeSelection *selection)
+{
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  g_return_if_fail (selection != NULL);
+  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+
+  g_object_ref (G_OBJECT (selection));
+
+  if (tree_view->priv->selection != NULL)
+    g_object_unref (G_OBJECT (tree_view->priv->selection));
+
+  tree_view->priv->selection = selection;
+}
+
+GtkAdjustment *
+gtk_tree_view_get_hadjustment (GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (tree_view != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+  return tree_view->priv->hadjustment;
+}
+
+void
+gtk_tree_view_set_hadjustment (GtkTreeView     *tree_view,
+                              GtkAdjustment *adjustment)
+{
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  gtk_tree_view_set_adjustments (tree_view,
+                                adjustment,
+                                tree_view->priv->vadjustment);
+}
+
+GtkAdjustment *
+gtk_tree_view_get_vadjustment (GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (tree_view != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+  return tree_view->priv->vadjustment;
+}
+
+void
+gtk_tree_view_set_vadjustment (GtkTreeView     *tree_view,
+                              GtkAdjustment *adjustment)
+{
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  gtk_tree_view_set_adjustments (tree_view,
+                                tree_view->priv->hadjustment,
+                                adjustment);
+}
+
+static void
+gtk_tree_view_set_adjustments (GtkTreeView     *tree_view,
+                              GtkAdjustment *hadj,
+                              GtkAdjustment *vadj)
+{
+  gboolean need_adjust = FALSE;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  if (hadj)
+    g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
+  else
+    hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+  if (vadj)
+    g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
+  else
+    vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+
+  if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
+    {
+      gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->hadjustment), tree_view);
+      gtk_object_unref (GTK_OBJECT (tree_view->priv->hadjustment));
+    }
+
+  if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
+    {
+      gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->vadjustment), tree_view);
+      gtk_object_unref (GTK_OBJECT (tree_view->priv->vadjustment));
+    }
+
+  if (tree_view->priv->hadjustment != hadj)
+    {
+      tree_view->priv->hadjustment = hadj;
+      gtk_object_ref (GTK_OBJECT (tree_view->priv->hadjustment));
+      gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment));
+
+      gtk_signal_connect (GTK_OBJECT (tree_view->priv->hadjustment), "value_changed",
+                         (GtkSignalFunc) gtk_tree_view_adjustment_changed,
+                         tree_view);
+      need_adjust = TRUE;
+    }
+
+  if (tree_view->priv->vadjustment != vadj)
+    {
+      tree_view->priv->vadjustment = vadj;
+      gtk_object_ref (GTK_OBJECT (tree_view->priv->vadjustment));
+      gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment));
+
+      gtk_signal_connect (GTK_OBJECT (tree_view->priv->vadjustment), "value_changed",
+                         (GtkSignalFunc) gtk_tree_view_adjustment_changed,
+                         tree_view);
+      need_adjust = TRUE;
+    }
+
+  if (need_adjust)
+    gtk_tree_view_adjustment_changed (NULL, tree_view);
+}
+
+
+/* Column and header operations */
+
+gboolean
+gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (tree_view != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
+
+  return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+}
+
+void
+gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
+                                  gboolean   headers_visible)
+{
+  gint x, y;
+  GList *list;
+  GtkTreeViewColumn *column;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible)
+    return;
+
+  if (headers_visible)
+    GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+  else
+    GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+
+  if (GTK_WIDGET_REALIZED (tree_view))
+    {
+      gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
+      if (headers_visible)
+       {
+         gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view));
+         for (list = tree_view->priv->column; list; list = list->next)
+           {
+             column = list->data;
+             gtk_widget_map (column->button);
+           }
+
+         for (list = tree_view->priv->column; list; list = list->next)
+           {
+             column = list->data;
+             if (column->visible == FALSE)
+               continue;
+             if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
+               {
+                 gdk_window_raise (column->window);
+                 gdk_window_show (column->window);
+               }
+             else
+               gdk_window_hide (column->window);
+           }
+         gdk_window_show (tree_view->priv->header_window);
+       }
+      else
+       {
+         gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
+         for (list = tree_view->priv->column; list; list = list->next)
+           {
+             column = list->data;
+             gtk_widget_unmap (column->button);
+           }
+         gdk_window_hide (tree_view->priv->header_window);
+       }
+    }
+
+  tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
+  tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
+  tree_view->priv->vadjustment->lower = 0;
+  tree_view->priv->vadjustment->upper = tree_view->priv->height;
+  gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
+
+  gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+}
+
+
+void
+gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
+{
+  gboolean dirty = FALSE;
+  GList *list;
+  GtkTreeViewColumn *column;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  for (list = tree_view->priv->column; list; list = list->next)
+    {
+      column = list->data;
+      if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+       continue;
+      column->dirty = TRUE;
+      dirty = TRUE;
+    }
+
+  if (dirty)
+    gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+}
+
+void
+gtk_tree_view_set_headers_active (GtkTreeView *tree_view,
+                                 gboolean   active)
+{
+  GList *list;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  g_return_if_fail (tree_view->priv->model != NULL);
+
+  for (list = tree_view->priv->column; list; list = list->next)
+    gtk_tree_view_column_set_header_active (GTK_TREE_VIEW_COLUMN (list->data), active);
+}
+
+gint
+gtk_tree_view_add_column (GtkTreeView   *tree_view,
+                         GtkTreeViewColumn *column)
+{
+  g_return_val_if_fail (tree_view != NULL, -1);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
+  g_return_val_if_fail (column != NULL, -1);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
+  g_return_val_if_fail (column->tree_view == NULL, -1);
+
+  tree_view->priv->column = g_list_append (tree_view->priv->column,
+                                          column);
+  column->tree_view = GTK_WIDGET (tree_view);
+  return tree_view->priv->columns++;
+}
+
+GtkTreeViewColumn *
+gtk_tree_view_get_column (GtkTreeView *tree_view,
+                         gint       n)
+{
+  g_return_val_if_fail (tree_view != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+  g_return_val_if_fail (tree_view->priv->model != NULL, NULL);
+  g_return_val_if_fail (n >= 0 || n < tree_view->priv->columns, NULL);
+
+  if (tree_view->priv->column == NULL)
+    return NULL;
+
+  return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->column, n)->data);
+}
+
+void
+gtk_tree_view_move_to (GtkTreeView *tree_view,
+                      GtkTreePath *path,
+                      gint       column,
+                      gfloat     row_align,
+                      gfloat     col_align)
+{
+  GtkRBNode *node = NULL;
+  GtkRBTree *tree = NULL;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  if (column < -1 || column > tree_view->priv->columns)
+    return;
+
+  row_align = CLAMP (row_align, 0, 1);
+  col_align = CLAMP (col_align, 0, 1);
+
+  if (path != NULL)
+    {
+      _gtk_tree_view_find_node (tree_view, path,
+                               &tree, &node);
+      if (node == NULL)
+       return;
+    }
+
+  if (tree_view->priv->hadjustment && column >= 0)
+    {
+      GtkTreeViewColumn *col;
+
+      col = g_list_nth (tree_view->priv->column, column)->data;
+      /* FIXME -- write  */
+    }
+}
+
+static void
+gtk_tree_view_expand_all_helper (GtkRBTree  *tree,
+                                GtkRBNode  *node,
+                                gpointer  data)
+{
+  GtkTreeView *tree_view = data;
+
+  if (node->children)
+    _gtk_rbtree_traverse (node->children,
+                         node->children->root,
+                         G_PRE_ORDER,
+                         gtk_tree_view_expand_all_helper,
+                         data);
+  else if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && node->children == NULL)
+    {
+      GtkTreePath *path;
+      GtkTreeNode tree_node;
+
+      node->children = _gtk_rbtree_new ();
+      node->children->parent_tree = tree;
+      node->children->parent_node = node;
+      path = _gtk_tree_view_find_path (tree_view, tree, node);
+      tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+      tree_node = gtk_tree_model_node_children (tree_view->priv->model, tree_node);
+      gtk_tree_view_build_tree (tree_view,
+                               node->children,
+                               tree_node,
+                               gtk_tree_path_get_depth (path) + 1,
+                               TRUE,
+                               GTK_WIDGET_REALIZED (tree_view));
+      gtk_tree_path_free (path);
+    }
+}
+
+void
+gtk_tree_view_expand_all (GtkTreeView *tree_view)
+{
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  g_return_if_fail (tree_view->priv->tree != NULL);
+
+  _gtk_rbtree_traverse (tree_view->priv->tree,
+                       tree_view->priv->tree->root,
+                       G_PRE_ORDER,
+                       gtk_tree_view_expand_all_helper,
+                       tree_view);
+
+  _gtk_tree_view_set_size (tree_view, -1,-1);
+}
+
+static void
+gtk_tree_view_collapse_all_helper (GtkRBTree  *tree,
+                                  GtkRBNode  *node,
+                                  gpointer  data)
+{
+  if (node->children)
+    {
+      GtkTreePath *path;
+      GtkTreeNode *tree_node;
+
+      path = _gtk_tree_view_find_path (GTK_TREE_VIEW (data),
+                                      node->children,
+                                      node->children->root);
+      tree_node = gtk_tree_model_get_node (GTK_TREE_VIEW (data)->priv->model, path);
+      gtk_tree_view_discover_dirty (GTK_TREE_VIEW (data),
+                                   node->children,
+                                   tree_node,
+                                   gtk_tree_path_get_depth (path));
+      _gtk_rbtree_remove (node->children);
+      gtk_tree_path_free (path);
+    }
+}
+
+void
+gtk_tree_view_collapse_all (GtkTreeView *tree_view)
+{
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  g_return_if_fail (tree_view->priv->tree != NULL);
+
+  _gtk_rbtree_traverse (tree_view->priv->tree,
+                       tree_view->priv->tree->root,
+                       G_PRE_ORDER,
+                       gtk_tree_view_collapse_all_helper,
+                       tree_view);
+
+  if (GTK_WIDGET_REALIZED (tree_view))
+    gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+}
diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h
new file mode 100644 (file)
index 0000000..bc90c5f
--- /dev/null
@@ -0,0 +1,105 @@
+/* gtktreeview.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_TREE_VIEW_H__
+#define __GTK_TREE_VIEW_H__
+
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeviewcolumn.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_VIEW             (gtk_tree_view_get_type ())
+#define GTK_TREE_VIEW(obj)             (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_VIEW, GtkTreeView))
+#define GTK_TREE_VIEW_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_VIEW, GtkTreeViewClass))
+#define GTK_IS_TREE_VIEW(obj)          (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_VIEW))
+#define GTK_IS_TREE_VIEW_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_VIEW))
+
+typedef struct _GtkTreeView        GtkTreeView;
+typedef struct _GtkTreeViewClass   GtkTreeViewClass;
+typedef struct _GtkTreeViewPrivate GtkTreeViewPrivate;
+
+typedef struct _GtkTreeSelection      GtkTreeSelection;
+typedef struct _GtkTreeSelectionClass GtkTreeSelectionClass;
+
+
+struct _GtkTreeView
+{
+  GtkContainer parent;
+
+  GtkTreeViewPrivate *priv;
+};
+
+struct _GtkTreeViewClass
+{
+  GtkContainerClass parent_class;
+
+  void (*set_scroll_adjustments) (GtkTreeView     *tree_view,
+                                 GtkAdjustment *hadjustment,
+                                 GtkAdjustment *vadjustment);
+  gint (*expand_row) (GtkTreeView *tree_view,
+                     GtkTreeNode *node);
+};
+
+/* Creators */
+GtkType                gtk_tree_view_get_type            (void);
+GtkWidget             *gtk_tree_view_new                 (void);
+GtkWidget             *gtk_tree_view_new_with_model      (GtkTreeModel      *model);
+GtkTreeModel          *gtk_tree_view_get_model           (GtkTreeView       *tree_view);
+void                   gtk_tree_view_set_model           (GtkTreeView       *tree_view,
+                                                         GtkTreeModel      *tree_model);
+GtkTreeSelection      *gtk_tree_view_get_selection       (GtkTreeView       *tree_view);
+void                   gtk_tree_view_set_selection       (GtkTreeView       *tree_view,
+                                                         GtkTreeSelection  *selection);
+GtkAdjustment         *gtk_tree_view_get_hadjustment     (GtkTreeView       *layout);
+void                   gtk_tree_view_set_hadjustment     (GtkTreeView       *layout,
+                                                         GtkAdjustment     *adjustment);
+GtkAdjustment         *gtk_tree_view_get_vadjustment     (GtkTreeView       *layout);
+void                   gtk_tree_view_set_vadjustment     (GtkTreeView       *layout,
+                                                         GtkAdjustment     *adjustment);
+gboolean               gtk_tree_view_get_headers_visible (GtkTreeView       *tree_view);
+void                   gtk_tree_view_set_headers_visible (GtkTreeView       *tree_view,
+                                                         gboolean           headers_visible);
+void                   gtk_tree_view_columns_autosize    (GtkTreeView       *tree_view);
+void                   gtk_tree_view_set_headers_active  (GtkTreeView       *tree_view,
+                                                         gboolean           active);
+gint                   gtk_tree_view_add_column          (GtkTreeView       *tree_view,
+                                                         GtkTreeViewColumn *column);
+GtkTreeViewColumn     *gtk_tree_view_get_column          (GtkTreeView       *tree_view,
+                                                         gint               n);
+
+/* Actions */
+void                   gtk_tree_view_move_to             (GtkTreeView       *tree_view,
+                                                         GtkTreePath       *path,
+                                                         gint               column,
+                                                         gfloat             row_align,
+                                                         gfloat             col_align);
+void                   gtk_tree_view_expand_all          (GtkTreeView       *tree_view);
+void                   gtk_tree_view_collapse_all        (GtkTreeView       *tree_view);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_VIEW_H__ */
+
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
new file mode 100644 (file)
index 0000000..248e655
--- /dev/null
@@ -0,0 +1,633 @@
+/* gtktreeviewcolumn.c
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreeviewcolumn.h"
+#include "gtktreeprivate.h"
+#include "gtksignal.h"
+#include "gtkbutton.h"
+#include "gtkalignment.h"
+
+enum {
+  CLICKED,
+  LAST_SIGNAL
+};
+
+
+static void gtk_tree_view_column_init            (GtkTreeViewColumn      *tree_column);
+static void gtk_tree_view_column_class_init      (GtkTreeViewColumnClass *klass);
+static void gtk_tree_view_column_set_attributesv (GtkTreeViewColumn      *tree_column,
+                                                 va_list                 args);
+static void gtk_real_tree_column_clicked         (GtkTreeViewColumn      *tree_column);
+
+
+static GtkObjectClass *parent_class = NULL;
+static guint tree_column_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_tree_view_column_get_type (void)
+{
+  static GtkType tree_column_type = 0;
+
+  if (!tree_column_type)
+    {
+      static const GTypeInfo tree_column_info =
+      {
+       sizeof (GtkTreeViewColumnClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       (GClassInitFunc) gtk_tree_view_column_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkTreeViewColumn),
+       0,
+       (GInstanceInitFunc) gtk_tree_view_column_init,
+      };
+
+      tree_column_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeViewColumn", &tree_column_info);
+    }
+
+  return tree_column_type;
+}
+
+static void
+gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
+{
+  GtkObjectClass *object_class;
+
+  object_class = (GtkObjectClass*) class;
+
+  parent_class = g_type_class_peek_parent (class);
+
+  tree_column_signals[CLICKED] =
+    gtk_signal_new ("clicked",
+                   GTK_RUN_FIRST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkTreeViewColumnClass, clicked),
+                   gtk_marshal_NONE__NONE,
+                   GTK_TYPE_NONE, 0);
+
+  gtk_object_class_add_signals (object_class, tree_column_signals, LAST_SIGNAL);
+
+  class->clicked = gtk_real_tree_column_clicked;
+}
+
+static void
+gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
+{
+  tree_column->button = NULL;
+  tree_column->justification = GTK_JUSTIFY_LEFT;
+  tree_column->size = 0;
+  tree_column->min_width = -1;
+  tree_column->max_width = -1;
+  tree_column->cell = NULL;
+  tree_column->attributes = NULL;
+  tree_column->column_type = GTK_TREE_VIEW_COLUMN_AUTOSIZE;
+  tree_column->visible = TRUE;
+  tree_column->button_active = FALSE;
+  tree_column->dirty = TRUE;
+}
+
+/* used to make the buttons 'unclickable' */
+
+static gint
+gtk_tree_view_passive_func (GtkWidget *widget,
+                           GdkEvent  *event,
+                           gpointer   data)
+{
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  switch (event->type)
+    {
+    case GDK_MOTION_NOTIFY:
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+    case GDK_ENTER_NOTIFY:
+    case GDK_LEAVE_NOTIFY:
+      return TRUE;
+    default:
+      break;
+    }
+  return FALSE;
+}
+
+static void
+gtk_real_tree_column_clicked (GtkTreeViewColumn *tree_column)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+}
+
+GtkObject *
+gtk_tree_view_column_new (void)
+{
+  GtkObject *retval;
+
+  retval = GTK_OBJECT (gtk_type_new (GTK_TYPE_TREE_COLUMN));
+
+  return retval;
+}
+
+GtkObject *
+gtk_tree_view_column_new_with_attributes (gchar           *title,
+                                         GtkCellRenderer *cell,
+                                         ...)
+{
+  GtkObject *retval;
+  va_list args;
+
+  retval = gtk_tree_view_column_new ();
+
+  gtk_tree_view_column_set_title (GTK_TREE_VIEW_COLUMN (retval), title);
+  gtk_tree_view_column_set_cell_renderer (GTK_TREE_VIEW_COLUMN (retval), cell);
+
+  va_start (args, cell);
+  gtk_tree_view_column_set_attributesv (GTK_TREE_VIEW_COLUMN (retval),
+                                       args);
+  va_end (args);
+
+  return retval;
+}
+
+void
+gtk_tree_view_column_set_cell_renderer (GtkTreeViewColumn *tree_column,
+                                       GtkCellRenderer   *cell)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+  if (cell)
+    g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+  if (cell)
+    g_object_ref (G_OBJECT (cell));
+
+  if (tree_column->cell)
+    g_object_unref (G_OBJECT (tree_column->cell));
+
+  tree_column->cell = cell;
+}
+
+void
+gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column,
+                                   gchar             *attribute,
+                                   gint               column)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  tree_column->attributes = g_slist_prepend (tree_column->attributes, GINT_TO_POINTER (column));
+  tree_column->attributes = g_slist_prepend (tree_column->attributes, g_strdup (attribute));
+}
+
+static void
+gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column,
+                                     va_list            args)
+{
+  GSList *list;
+  gchar *attribute;
+  gint column;
+
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  attribute = va_arg (args, gchar *);
+
+  list = tree_column->attributes;
+
+  while (list && list->next)
+    {
+      g_free (list->data);
+      list = list->next->next;
+    }
+  g_slist_free (tree_column->attributes);
+  tree_column->attributes = NULL;
+
+  while (attribute != NULL)
+    {
+      column = va_arg (args, gint);
+      gtk_tree_view_column_add_attribute (tree_column,
+                                         attribute,
+                                         column);
+      attribute = va_arg (args, gchar *);
+    }
+}
+
+void
+gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column,
+                                    ...)
+{
+  va_list args;
+
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  va_start (args, tree_column);
+
+  gtk_tree_view_column_set_attributesv (tree_column, args);
+
+  va_end (args);
+}
+
+void
+gtk_tree_view_column_set_cell_data (GtkTreeViewColumn *tree_column,
+                                   GtkTreeModel      *tree_model,
+                                   GtkTreeNode        tree_node)
+{
+  GSList *list;
+  GValue value = { 0, };
+  GObject *cell;
+
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+  g_return_if_fail (tree_column->cell != NULL);
+
+  if (tree_column->func && (* tree_column->func) (tree_column,
+                                                 tree_model,
+                                                 tree_node,
+                                                 tree_column->func_data))
+    return;
+
+  cell = (GObject *) tree_column->cell;
+  list = tree_column->attributes;
+
+  while (list && list->next)
+    {
+      gtk_tree_model_node_get_value (tree_model,
+                                    tree_node,
+                                    GPOINTER_TO_INT (list->next->data),
+                                    &value);
+      g_object_set_param (cell, (gchar *) list->data, &value);
+      g_value_unset (&value);
+      list = list->next->next;
+    }
+}
+
+/* Options for manipulating the columns */
+
+void
+gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column,
+                                 gboolean           visible)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  if (tree_column->visible == visible)
+    return;
+
+  tree_column->visible = visible;
+
+  if (visible)
+    {
+      gtk_widget_show (tree_column->button);
+      gdk_window_show (tree_column->window);
+    }
+  else
+    {
+      gtk_widget_hide (tree_column->button);
+      gdk_window_hide (tree_column->window);
+    }
+
+  if (GTK_WIDGET_REALIZED (tree_column->tree_view))
+    {
+      _gtk_tree_view_set_size (GTK_TREE_VIEW (tree_column->tree_view), -1, -1);
+      gtk_widget_queue_resize (tree_column->tree_view);
+    }
+}
+
+gboolean
+gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
+
+  return tree_column->visible;
+}
+
+void
+gtk_tree_view_column_set_col_type (GtkTreeViewColumn     *tree_column,
+                                  GtkTreeViewColumnType  type)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  if (type == tree_column->column_type)
+    return;
+
+  tree_column->column_type = type;
+  switch (type)
+    {
+    case GTK_TREE_VIEW_COLUMN_AUTOSIZE:
+      tree_column->dirty = TRUE;
+    case GTK_TREE_VIEW_COLUMN_FIXED:
+      gdk_window_hide (tree_column->window);
+      break;
+    default:
+      gdk_window_show (tree_column->window);
+      gdk_window_raise (tree_column->window);
+      break;
+    }
+
+  gtk_widget_queue_resize (tree_column->tree_view);
+}
+
+gint
+gtk_tree_view_column_get_col_type (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, 0);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
+
+  return tree_column->column_type;
+}
+
+gint
+gtk_tree_view_column_get_preferred_size (GtkTreeViewColumn *tree_column)
+{
+  return 0;
+}
+
+gint
+gtk_tree_view_column_get_size (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, 0);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
+
+  return tree_column->size;
+}
+
+void
+gtk_tree_view_column_set_size (GtkTreeViewColumn *tree_column,
+                              gint               size)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+  g_return_if_fail (size > 0);
+
+  if (tree_column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE ||
+      tree_column->size == size)
+    return;
+
+  tree_column->size = size;
+
+  if (GTK_WIDGET_REALIZED (tree_column->tree_view))
+    gtk_widget_queue_resize (tree_column->tree_view);
+}
+
+void
+gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column,
+                                   gint               min_width)
+{
+  gint real_min_width;
+
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+  g_return_if_fail (min_width >= -1);
+
+  if (min_width == tree_column->min_width)
+    return;
+
+  real_min_width = (tree_column->min_width == -1) ?
+    tree_column->button->requisition.width : tree_column->min_width;
+
+  /* We want to queue a resize if the either the old min_size or the
+   * new min_size determined the size of the column */
+  if (GTK_WIDGET_REALIZED (tree_column->tree_view) &&
+      ((tree_column->min_width > tree_column->size) ||
+       (tree_column->min_width == -1 &&
+       tree_column->button->requisition.width > tree_column->size) ||
+       (min_width > tree_column->size) ||
+       (min_width == -1 &&
+       tree_column->button->requisition.width > tree_column->size)))
+    gtk_widget_queue_resize (tree_column->tree_view);
+
+  if (tree_column->max_width != -1 &&
+      tree_column->max_width < real_min_width)
+    tree_column->max_width = real_min_width;
+
+  tree_column->min_width = min_width;
+}
+
+gint
+gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, -1);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1);
+
+  return tree_column->min_width;
+}
+
+void
+gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column,
+                                   gint               max_width)
+{
+  gint real_min_width;
+
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+  g_return_if_fail (max_width >= -1);
+
+  if (max_width == tree_column->max_width)
+    return;
+
+  real_min_width = tree_column->min_width == -1 ?
+    tree_column->button->requisition.width : tree_column->min_width;
+
+  if (GTK_WIDGET_REALIZED (tree_column->tree_view) &&
+      ((tree_column->max_width < tree_column->size) ||
+       (max_width != -1 && max_width < tree_column->size)))
+    gtk_widget_queue_resize (tree_column->tree_view);
+
+  tree_column->max_width = max_width;
+
+  if (real_min_width > max_width)
+    tree_column->min_width = max_width;
+}
+
+gint
+gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, -1);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1);
+
+  return tree_column->max_width;
+}
+
+void
+gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column,
+                               gchar         *title)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  g_free (tree_column->title);
+  if (title)
+    tree_column->title = g_strdup (title);
+  else
+    tree_column->title = NULL;
+}
+
+gchar *
+gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
+
+  return tree_column->title;
+}
+
+void
+gtk_tree_view_column_set_header_active (GtkTreeViewColumn *tree_column,
+                                       gboolean           active)
+{
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  if (!tree_column->button)
+    return;
+
+  if (tree_column->button_active == active)
+    return;
+
+  tree_column->button_active = active;
+  if (active)
+    {
+      gtk_signal_disconnect_by_func (GTK_OBJECT (tree_column->button),
+                                    (GtkSignalFunc) gtk_tree_view_passive_func,
+                                    NULL);
+
+      GTK_WIDGET_SET_FLAGS (tree_column->button, GTK_CAN_FOCUS);
+
+      if (GTK_WIDGET_VISIBLE (tree_column->tree_view))
+       gtk_widget_queue_draw (tree_column->button);
+    }
+  else
+    {
+      gtk_signal_connect (GTK_OBJECT (tree_column->button),
+                         "event",
+                         (GtkSignalFunc) gtk_tree_view_passive_func,
+                         NULL);
+
+      GTK_WIDGET_UNSET_FLAGS (tree_column->button, GTK_CAN_FOCUS);
+
+      if (GTK_WIDGET_VISIBLE (tree_column->tree_view))
+       gtk_widget_queue_draw (tree_column->button);
+    }
+}
+
+void
+gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column,
+                                GtkWidget         *widget)
+{
+#if 0
+  gint new_button = 0;
+  GtkWidget *old_widget;
+
+  g_return_if_fail (tree_view != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  if (column < 0 || column >= tree_view->priv->columns)
+    return;
+
+  /* if the column button doesn't currently exist,
+   * it has to be created first */
+  if (!column->button)
+    {
+      column_button_create (tree_view, column);
+      new_button = 1;
+    }
+
+  column_title_new (clist, column, NULL);
+
+  /* remove and destroy the old widget */
+  old_widget = GTK_BIN (clist->column[column].button)->child;
+  if (old_widget)
+    gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
+                         old_widget);
+
+  /* add and show the widget */
+  if (widget)
+    {
+      gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
+      gtk_widget_show (widget);
+    }
+
+  /* if this button didn't previously exist, then the
+   * column button positions have to be re-computed */
+  if (GTK_WIDGET_VISIBLE (clist) && new_button)
+    size_allocate_title_buttons (clist);
+#endif
+}
+
+GtkWidget *
+gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column)
+{
+  g_return_val_if_fail (tree_column != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
+
+  if (tree_column->button)
+    return GTK_BUTTON (tree_column->button)->child;
+
+  return NULL;
+}
+
+void
+gtk_tree_view_column_set_justification (GtkTreeViewColumn *tree_column,
+                                       GtkJustification   justification)
+{
+  GtkWidget *alignment;
+
+  g_return_if_fail (tree_column != NULL);
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+  if (tree_column->justification == justification)
+    return;
+
+  tree_column->justification = justification;
+
+  /* change the alignment of the button title if it's not a
+   * custom widget */
+  alignment = GTK_BIN (tree_column->button)->child;
+
+  if (GTK_IS_ALIGNMENT (alignment))
+    {
+      switch (tree_column->justification)
+       {
+       case GTK_JUSTIFY_LEFT:
+         gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
+         break;
+
+       case GTK_JUSTIFY_RIGHT:
+         gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
+         break;
+
+       case GTK_JUSTIFY_CENTER:
+         gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
+         break;
+
+       case GTK_JUSTIFY_FILL:
+         gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
+         break;
+
+       default:
+         break;
+       }
+    }
+}
diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h
new file mode 100644 (file)
index 0000000..765b81d
--- /dev/null
@@ -0,0 +1,137 @@
+/* gtktreeviewcolumn.h
+ * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_VIEW_COLUMN_H__
+#define __GTK_TREE_VIEW_COLUMN_H__
+
+#include <gtk/gtkobject.h>
+#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_COLUMN                (gtk_tree_view_column_get_type ())
+#define GTK_TREE_VIEW_COLUMN(obj)           (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_COLUMN, GtkTreeViewColumn))
+#define GTK_TREE_VIEW_COLUMN_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_COLUMN, GtkTreeViewColumnClass))
+#define GTK_IS_TREE_VIEW_COLUMN(obj)        (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_COLUMN))
+#define GTK_IS_TREE_VIEW_COLUMN_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_COLUMN))
+
+typedef enum
+{
+  GTK_TREE_VIEW_COLUMN_RESIZEABLE,
+  GTK_TREE_VIEW_COLUMN_AUTOSIZE,
+  GTK_TREE_VIEW_COLUMN_FIXED
+} GtkTreeViewColumnType;
+
+typedef struct _GtkTreeViewColumn      GtkTreeViewColumn;
+typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass;
+
+typedef gboolean (* GtkTreeViewColumnFunc) (GtkTreeViewColumn *tree_column,
+                                           GtkTreeModel      *tree_model,
+                                           GtkTreeNode        tree_node,
+                                           gpointer           data);
+
+struct _GtkTreeViewColumn
+{
+  GtkObject parent;
+
+  GtkWidget *tree_view;
+  GtkWidget *button;
+  GdkWindow *window;
+  GtkJustification justification;
+
+  gint id;
+  gint size;
+  gint min_width;
+  gint max_width;
+
+  GtkTreeViewColumnFunc *func;
+  gpointer func_data;
+  gchar *title;
+  GtkCellRenderer *cell;
+  GSList *attributes;
+  GtkTreeViewColumnType column_type;
+  guint visible       : 1;
+  guint button_active : 1;
+  guint dirty         : 1;
+};
+
+struct _GtkTreeViewColumnClass
+{
+  GtkObjectClass parent_class;
+
+  void (*clicked) (GtkTreeViewColumn *tree_column);
+};
+
+
+GtkType          gtk_tree_view_column_get_type            (void);
+GtkObject       *gtk_tree_view_column_new                 (void);
+GtkObject       *gtk_tree_view_column_new_with_attributes (gchar                 *title,
+                                                          GtkCellRenderer       *cell,
+                                                          ...);
+void             gtk_tree_view_column_set_cell_renderer   (GtkTreeViewColumn     *tree_column,
+                                                          GtkCellRenderer       *cell);
+void             gtk_tree_view_column_add_attribute       (GtkTreeViewColumn     *tree_column,
+                                                          gchar                 *attribute,
+                                                          gint                   column);
+void             gtk_tree_view_column_set_attributes      (GtkTreeViewColumn     *tree_column,
+                                                          ...);
+void             gtk_tree_view_column_set_cell_data       (GtkTreeViewColumn     *tree_column,
+                                                          GtkTreeModel          *tree_model,
+                                                          GtkTreeNode            tree_node);
+void             gtk_tree_view_column_set_visible         (GtkTreeViewColumn     *tree_column,
+                                                          gboolean               visible);
+gboolean         gtk_tree_view_column_get_visible         (GtkTreeViewColumn     *tree_column);
+void             gtk_tree_view_column_set_col_type        (GtkTreeViewColumn     *tree_column,
+                                                          GtkTreeViewColumnType  type);
+gint             gtk_tree_view_column_get_col_type        (GtkTreeViewColumn     *tree_column);
+gint             gtk_tree_view_column_get_preferred_size  (GtkTreeViewColumn     *tree_column);
+gint             gtk_tree_view_column_get_size            (GtkTreeViewColumn     *tree_column);
+void             gtk_tree_view_column_set_size            (GtkTreeViewColumn     *tree_column,
+                                                          gint                   width);
+void             gtk_tree_view_column_set_min_width       (GtkTreeViewColumn     *tree_column,
+                                                          gint                   min_width);
+gint             gtk_tree_view_column_get_min_width       (GtkTreeViewColumn     *tree_column);
+void             gtk_tree_view_column_set_max_width       (GtkTreeViewColumn     *tree_column,
+                                                          gint                   max_width);
+gint             gtk_tree_view_column_get_max_width       (GtkTreeViewColumn     *tree_column);
+
+
+/* Options for manipulating the column headers
+ */
+void             gtk_tree_view_column_set_title           (GtkTreeViewColumn     *tree_column,
+                                                          gchar                 *title);
+gchar           *gtk_tree_view_column_get_title           (GtkTreeViewColumn     *tree_column);
+void             gtk_tree_view_column_set_header_active   (GtkTreeViewColumn     *tree_column,
+                                                          gboolean               active);
+void             gtk_tree_view_column_set_widget          (GtkTreeViewColumn     *tree_column,
+                                                          GtkWidget             *widget);
+GtkWidget       *gtk_tree_view_column_get_widget          (GtkTreeViewColumn     *tree_column);
+void             gtk_tree_view_column_set_justification   (GtkTreeViewColumn     *tree_column,
+                                                          GtkJustification       justification);
+GtkJustification gtk_tree_view_column_get_justification   (GtkTreeViewColumn     *tree_column);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_TREE_VIEW_COLUMN_H__ */
diff --git a/gtk/treestoretest.c b/gtk/treestoretest.c
new file mode 100644 (file)
index 0000000..a8bec54
--- /dev/null
@@ -0,0 +1,206 @@
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+GtkObject *model;
+
+static void
+row_selected (GtkTreeView  *tree_view,
+             GtkTreeModel *tree_model,
+             GtkTreeNode  *node,
+             GtkWidget    *button)
+{
+  gtk_widget_set_sensitive (button, TRUE);
+}
+
+static void
+row_unselected (GtkTreeView  *tree_view,
+               GtkTreeModel *tree_model,
+               GtkTreeNode  *node,
+               GtkWidget    *button)
+{
+  gtk_widget_set_sensitive (button, FALSE);
+}
+
+static GtkTreeNode *
+node_new ()
+{
+  static GValue value = {0, };
+  static gint i = 0;
+  gchar *str;
+  GtkTreeNode *node = gtk_tree_store_node_new ();
+
+  g_value_init (&value, G_TYPE_STRING);
+  str = g_strdup_printf ("FOO: %d", i++);
+  g_value_set_string (&value, str);
+  g_free (str);
+  gtk_tree_store_node_set_cell (GTK_TREE_STORE (model), node, 0, &value);
+  g_value_unset (&value);
+
+  return node;
+}
+
+static void
+node_remove (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_remove (GTK_TREE_STORE (model),
+                             selected);
+}
+
+static void
+node_insert (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkWidget *entry;
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+
+  entry = gtk_object_get_user_data (GTK_OBJECT (button));
+  gtk_tree_store_node_insert (GTK_TREE_STORE (model),
+                             selected,
+                             atoi (gtk_entry_get_text (GTK_ENTRY (entry))),
+                             node_new ());
+}
+
+static void
+node_insert_before  (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_insert_before (GTK_TREE_STORE (model),
+                                   NULL,
+                                   selected,
+                                   node_new ());
+}
+
+static void
+node_insert_after (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_insert_after (GTK_TREE_STORE (model),
+                                   NULL,
+                                   selected,
+                                   node_new ());
+}
+
+static void
+node_prepend (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_prepend (GTK_TREE_STORE (model),
+                              selected,
+                              node_new ());
+}
+
+static void
+node_append (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_append (GTK_TREE_STORE (model),
+                             selected,
+                             node_new ());
+}
+
+static void
+make_window ()
+{
+  GtkWidget *window;
+  GtkWidget *vbox;
+  GtkWidget *hbox, *entry;
+  GtkWidget *button;
+  GtkWidget *scrolled_window;
+  GtkWidget *tree_view;
+  GtkObject *column;
+  GtkCellRenderer *cell;
+  GtkObject *selection;
+
+  /* Make the Widgets/Objects */
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  vbox = gtk_vbox_new (FALSE, 8);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+  gtk_window_set_default_size (GTK_WINDOW (window), 300, 350);
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+  selection = GTK_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_selection_set_type (GTK_TREE_SELECTION (selection), GTK_TREE_SELECTION_SINGLE);
+
+  /* Put them together */
+  gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                 GTK_POLICY_AUTOMATIC,
+                                 GTK_POLICY_AUTOMATIC);
+  gtk_signal_connect (GTK_OBJECT (window), "destroy", gtk_main_quit, NULL);
+
+  /* buttons */
+  button = gtk_button_new_with_label ("gtk_tree_store_node_remove");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_selected", row_selected, button);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_unselected", row_unselected, button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_remove, tree_view);
+  gtk_widget_set_sensitive (button, FALSE);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_insert");
+  hbox = gtk_hbox_new (FALSE, 8);
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+  gtk_object_set_user_data (GTK_OBJECT (button), entry);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert, tree_view);
+
+  
+  button = gtk_button_new_with_label ("gtk_tree_store_node_insert_before");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_selected", row_selected, button);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_unselected", row_unselected, button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_before, tree_view);
+  gtk_widget_set_sensitive (button, FALSE);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_insert_after");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_selected", row_selected, button);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_unselected", row_unselected, button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_after, tree_view);
+  gtk_widget_set_sensitive (button, FALSE);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_prepend");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_prepend, tree_view);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_append");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_append, tree_view);
+
+  /* The selected column */
+  cell = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes ("nodes", cell, "text", 0, NULL);
+  gtk_tree_view_add_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (column));
+
+  /* Show it all */
+  gtk_widget_show_all (window);
+}
+
+int
+main (int argc, char *argv[])
+{
+  gtk_init (&argc, &argv);
+
+  model = gtk_tree_store_new_with_values (2, G_TYPE_STRING, G_TYPE_STRING);
+
+  make_window ();
+  make_window ();
+
+  /* A few to start */
+  gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+  gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+  gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/tests/treestoretest.c b/tests/treestoretest.c
new file mode 100644 (file)
index 0000000..a8bec54
--- /dev/null
@@ -0,0 +1,206 @@
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+GtkObject *model;
+
+static void
+row_selected (GtkTreeView  *tree_view,
+             GtkTreeModel *tree_model,
+             GtkTreeNode  *node,
+             GtkWidget    *button)
+{
+  gtk_widget_set_sensitive (button, TRUE);
+}
+
+static void
+row_unselected (GtkTreeView  *tree_view,
+               GtkTreeModel *tree_model,
+               GtkTreeNode  *node,
+               GtkWidget    *button)
+{
+  gtk_widget_set_sensitive (button, FALSE);
+}
+
+static GtkTreeNode *
+node_new ()
+{
+  static GValue value = {0, };
+  static gint i = 0;
+  gchar *str;
+  GtkTreeNode *node = gtk_tree_store_node_new ();
+
+  g_value_init (&value, G_TYPE_STRING);
+  str = g_strdup_printf ("FOO: %d", i++);
+  g_value_set_string (&value, str);
+  g_free (str);
+  gtk_tree_store_node_set_cell (GTK_TREE_STORE (model), node, 0, &value);
+  g_value_unset (&value);
+
+  return node;
+}
+
+static void
+node_remove (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_remove (GTK_TREE_STORE (model),
+                             selected);
+}
+
+static void
+node_insert (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkWidget *entry;
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+
+  entry = gtk_object_get_user_data (GTK_OBJECT (button));
+  gtk_tree_store_node_insert (GTK_TREE_STORE (model),
+                             selected,
+                             atoi (gtk_entry_get_text (GTK_ENTRY (entry))),
+                             node_new ());
+}
+
+static void
+node_insert_before  (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_insert_before (GTK_TREE_STORE (model),
+                                   NULL,
+                                   selected,
+                                   node_new ());
+}
+
+static void
+node_insert_after (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_insert_after (GTK_TREE_STORE (model),
+                                   NULL,
+                                   selected,
+                                   node_new ());
+}
+
+static void
+node_prepend (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_prepend (GTK_TREE_STORE (model),
+                              selected,
+                              node_new ());
+}
+
+static void
+node_append (GtkWidget *button, GtkTreeView *tree_view)
+{
+  GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_store_node_append (GTK_TREE_STORE (model),
+                             selected,
+                             node_new ());
+}
+
+static void
+make_window ()
+{
+  GtkWidget *window;
+  GtkWidget *vbox;
+  GtkWidget *hbox, *entry;
+  GtkWidget *button;
+  GtkWidget *scrolled_window;
+  GtkWidget *tree_view;
+  GtkObject *column;
+  GtkCellRenderer *cell;
+  GtkObject *selection;
+
+  /* Make the Widgets/Objects */
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  vbox = gtk_vbox_new (FALSE, 8);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+  gtk_window_set_default_size (GTK_WINDOW (window), 300, 350);
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+  selection = GTK_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+  gtk_tree_selection_set_type (GTK_TREE_SELECTION (selection), GTK_TREE_SELECTION_SINGLE);
+
+  /* Put them together */
+  gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                 GTK_POLICY_AUTOMATIC,
+                                 GTK_POLICY_AUTOMATIC);
+  gtk_signal_connect (GTK_OBJECT (window), "destroy", gtk_main_quit, NULL);
+
+  /* buttons */
+  button = gtk_button_new_with_label ("gtk_tree_store_node_remove");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_selected", row_selected, button);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_unselected", row_unselected, button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_remove, tree_view);
+  gtk_widget_set_sensitive (button, FALSE);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_insert");
+  hbox = gtk_hbox_new (FALSE, 8);
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+  gtk_object_set_user_data (GTK_OBJECT (button), entry);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert, tree_view);
+
+  
+  button = gtk_button_new_with_label ("gtk_tree_store_node_insert_before");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_selected", row_selected, button);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_unselected", row_unselected, button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_before, tree_view);
+  gtk_widget_set_sensitive (button, FALSE);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_insert_after");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_selected", row_selected, button);
+  gtk_signal_connect (GTK_OBJECT (selection),
+                     "row_unselected", row_unselected, button);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_after, tree_view);
+  gtk_widget_set_sensitive (button, FALSE);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_prepend");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_prepend, tree_view);
+
+  button = gtk_button_new_with_label ("gtk_tree_store_node_append");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect (GTK_OBJECT (button), "clicked", node_append, tree_view);
+
+  /* The selected column */
+  cell = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes ("nodes", cell, "text", 0, NULL);
+  gtk_tree_view_add_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (column));
+
+  /* Show it all */
+  gtk_widget_show_all (window);
+}
+
+int
+main (int argc, char *argv[])
+{
+  gtk_init (&argc, &argv);
+
+  model = gtk_tree_store_new_with_values (2, G_TYPE_STRING, G_TYPE_STRING);
+
+  make_window ();
+  make_window ();
+
+  /* A few to start */
+  gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+  gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+  gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+
+  gtk_main ();
+
+  return 0;
+}